static private bool BoxHitsTerrain(NativeArray <TerrainSystem.AABB> blockBoxes, int2 terrainSize,
                                           TerrainSystem.AABB box)
        {
            // check nearby boxes -- TODO bias by half a box
            int xMin = (int)math.floor((box.Min.x + 0.5f) / TerrainSystem.BOX_SPACING);
            int xMax = (int)math.floor((box.Max.x + 0.5f) / TerrainSystem.BOX_SPACING);
            int zMin = (int)math.floor((box.Min.z + 0.5f) / TerrainSystem.BOX_SPACING);
            int zMax = (int)math.floor((box.Max.z + 0.5f) / TerrainSystem.BOX_SPACING);

            xMin = math.max(0, xMin);
            xMax = math.min(terrainSize.x - 1, xMax);
            zMin = math.max(0, zMin);
            zMax = math.min(terrainSize.y - 1, zMax);

            for (int z = zMin; z <= zMax; z++)
            {
                for (int x = xMin; x <= xMax; x++)
                {
                    int blockIndex = TerrainSystem.BlockCoordsToIndex(new int2(x, z), terrainSize);
                    TerrainSystem.AABB blockBox = blockBoxes[blockIndex];
                    bool intersects             = !(box.Max.x <blockBox.Min.x || box.Min.x> blockBox.Max.x ||
                                                    box.Max.y <blockBox.Min.y || box.Min.y> blockBox.Max.y ||
                                                    box.Max.z <blockBox.Min.z || box.Min.z> blockBox.Max.z);
                    if (intersects)
                    {
                        return(true);
                    }
                }
            }

            // TODO: check tanks
            return(false);
        }
Пример #2
0
        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            if (_options == null)
            {
                _options = GameObject.Find("Options").GetComponent <Options>();
            }
            // Determine which block is under the mouse cursor.
            float   meanBlockHeight = (_options.terrainHeightMin + _options.terrainHeightMax) / 2;
            Vector3 mouseWorldPos   = new Vector3(0, meanBlockHeight, 0);
            Ray     ray             = Camera.main.ScreenPointToRay(Input.mousePosition);
            float   t = (meanBlockHeight - ray.origin.y) / ray.direction.y;

            mouseWorldPos.x = ray.origin.x + t * ray.direction.x;
            mouseWorldPos.z = ray.origin.z + t * ray.direction.z;
            inputDeps       = JobHandle.CombineDependencies(inputDeps, _terrain.UpdateCacheJob);
            int2 mouseBoxCoords = TerrainSystem.PositionToBlockCoords(mouseWorldPos.x, mouseWorldPos.z, _terrain.TerrainSize);

            return(new UpdateBounceJob
            {
                ElapsedTime = Time.time,
                MouseBoxCoords = mouseBoxCoords,
                TerrainSize = new int2(_options.terrainSizeX, _options.terrainSizeZ),
                BlockHeights = _terrain.CachedBlockHeights,
                IsBlockOccupied = _terrain.CachedIsBlockOccupied,
            }.Schedule(_playerQuery, inputDeps));
        }
            public unsafe void Execute(Entity entity, int entityIndex, ref Translation cannonballPos, ref ArcState arc)
            {
                float t = ElapsedTime - arc.StartTime;

                if (t >= 0 && t < arc.Duration)
                {
                    // Test for collisions with the player
                    const float COLLISION_DISTANCE_SQ =
                        (PLAYER_RADIUS + TankFireSystem.CANNONBALL_RADIUS) *
                        (PLAYER_RADIUS + TankFireSystem.CANNONBALL_RADIUS);
                    if (math.lengthsq(cannonballPos.Value - PlayerCenterPosition) < COLLISION_DISTANCE_SQ)
                    {
                        if (!EnableInvincibility)
                        {
                            // game over! add a NewGame entity to trigger the NewGameSystem to restart.
                            Entity gameOverEntity = CommandBuffer.CreateEntity(entityIndex);
                            CommandBuffer.AddComponent(entityIndex, gameOverEntity, new NewGameTag());
                        }
                    }

                    // Get the current height along the parabola & update cannonball pos
                    float s = t / arc.Duration;
                    cannonballPos.Value = new float3(
                        math.lerp(arc.StartPos.x, arc.EndPos.x, s),
                        Parabola.Solve(arc.Parabola.x, arc.Parabola.y, arc.Parabola.z, s),
                        math.lerp(arc.StartPos.z, arc.EndPos.z, s)
                        );
                }
                else if (t >= arc.Duration)
                {
                    // Cannonball has reached the end of its path; damage the block it hit
                    // TODO(@cort): If the destination block has been damaged/lowered since the shot was fired, the shot will "land" in the air above the block. Original game does this too.
                    int2   boxCoords    = TerrainSystem.PositionToBlockCoords(cannonballPos.Value.x, cannonballPos.Value.z, TerrainSize);
                    int    boxIndex     = TerrainSystem.BlockCoordsToIndex(boxCoords, TerrainSize);
                    Entity boxEntity    = BlockEntities[boxIndex];
                    int *  blockHitsPtr = (int *)BlockHitCounts.GetUnsafePtr() + boxIndex;
                    int    newCount     = Interlocked.Increment(ref *blockHitsPtr);
                    if (newCount == 1)
                    {
                        CommandBuffer.AddComponent(entityIndex, boxEntity, new UpdateBlockTransformTag());
                    }
                    // TODO(@cort): There is a (relatively benign) race condition here. There's no guarantee that the last job
                    //              to update the hit counts array will also write the last command that updates the BlockHeight component.
                    //              This means that if >1 cannonball hits a block in a single frame, some of the hits may not damage the block.
                    //              One solution would be commands that allow mutating component values rather than setting them to a value
                    //              known at record time. https://github.com/Unity-Technologies/dots/issues/632
                    float currentBoxHeight = BlockHeights[boxIndex];
                    CommandBuffer.SetComponent(entityIndex, boxEntity,
                                               new BlockHeight
                    {
                        Value = math.max(BLOCK_HEIGHT_MIN, currentBoxHeight - newCount * BoxHeightDamage)
                    });
                    // destroy the cannonball
                    CommandBuffer.DestroyEntity(entityIndex, entity);
                }
            }
 protected override void OnCreateManager()
 {
     _cannonballQuery = GetEntityQuery(new ComponentType[] {
         ComponentType.ReadWrite <Translation>(),
         ComponentType.ReadWrite <ArcState>(),
         ComponentType.ReadWrite <Scale>(),
     });
     _terrain        = World.GetOrCreateSystem <TerrainSystem>();
     _barrier        = World.GetOrCreateSystem <BeginSimulationEntityCommandBufferSystem>();
     _playerPosCache = World.GetOrCreateSystem <PlayerPositionCacheSystem>();
 }
Пример #5
0
 protected override void OnCreateManager()
 {
     _playerQuery = GetEntityQuery(new ComponentType[]
     {
         ComponentType.ReadWrite <Translation>(),
         ComponentType.ReadWrite <ArcState>(),
         ComponentType.Exclude <Scale>(),
     });
     _terrain       = World.GetOrCreateSystem <TerrainSystem>();
     _tankAimSystem = World.GetOrCreateSystem <TankAimSystem>();
 }
 protected override void OnCreateManager()
 {
     _terrain        = World.GetOrCreateSystem <TerrainSystem>();
     _playerPosCache = World.GetOrCreateSystem <PlayerPositionCacheSystem>();
     _barrier        = World.GetOrCreateSystem <BeginSimulationEntityCommandBufferSystem>();
 }
            public void Execute(Entity _, int index, [ReadOnly] ref LocalToWorld localToWorld, ref TankFireCountdown countdown,
                                ref Rotation rotation)
            {
                // launching cannonball
                countdown.SecondsUntilFire -= DeltaTime;
                if (countdown.SecondsUntilFire <= 0)
                {
                    countdown.SecondsUntilFire = math.fmod(countdown.SecondsUntilFire, FirePeriod) + FirePeriod;
                    // start and end positions
                    int2   playerBoxCoords = TerrainSystem.PositionToBlockCoords(PlayerPosition.x, PlayerPosition.z, TerrainSize);
                    int    playerBoxIndex  = TerrainSystem.BlockCoordsToIndex(playerBoxCoords, TerrainSize);
                    float  playerBoxHeight = BlockHeights[playerBoxIndex];
                    float3 startPos        = math.mul(localToWorld.Value, new float4(0, 0, 0, 1)).xyz;
                    float3 endPos          = TerrainSystem.BlockCoordsToPosition(playerBoxCoords, playerBoxHeight, TerrainSize);
                    endPos.y += CANNONBALL_RADIUS + 0.01f; // extra 0.01 to avoid false positive collisions at endPos
                    float distance = math.distance(endPos.xz, startPos.xz);
                    float duration = distance / CANNONBALL_SPEED;
                    if (duration < 0.0001f)
                    {
                        duration = 1.0f;
                    }

                    // binary searching to determine height of cannonball arc
                    float low = math.max(startPos.y, endPos.y);
                    float high = low * 2;
                    float paraA, paraB, paraC;

                    // look for height of arc that won't hit boxes
                    while (true)
                    {
                        Parabola.Create(startPos.y, high, endPos.y, out paraA, out paraB, out paraC);
                        if (!PathHitsTerrain(BlockBoundingBoxes, TerrainSize, startPos, endPos, paraA, paraB, paraC))
                        {
                            // high enough
                            break;
                        }

                        // not high enough.  Double value
                        low   = high;
                        high *= 2;
                        // failsafe
                        if (high > 9999)
                        {
                            return; // skip launch
                        }
                    }
                    // do binary searches to narrow down
                    while (high - low > CANNONBALL_PARABOLA_PRECISION)
                    {
                        float mid = (low + high) / 2;
                        Parabola.Create(startPos.y, mid, endPos.y, out paraA, out paraB, out paraC);
                        if (PathHitsTerrain(BlockBoundingBoxes, TerrainSize, startPos, endPos, paraA, paraB, paraC))
                        {
                            // not high enough
                            low = mid;
                        }
                        else
                        {
                            // too high
                            high = mid;
                        }
                    }

                    // launch with calculated height
                    float height = (low + high) / 2;
                    Parabola.Create(startPos.y, height, endPos.y, out paraA, out paraB, out paraC);

                    Entity ballEntity = CommandBuffer.Instantiate(index, CannonballPrefab);
                    CommandBuffer.SetComponent(index, ballEntity, new Translation {
                        Value = startPos
                    });
                    CommandBuffer.SetComponent(index, ballEntity, new ArcState
                    {
                        Parabola  = new float3(paraA, paraB, paraC),
                        StartTime = ElapsedTime,
                        Duration  = duration,
                        StartPos  = startPos,
                        EndPos    = endPos,
                    });

                    // set cannon rotation
                    {
                        const float t             = 0.01f;
                        float       altitudeAngle = -math.atan2(
                            Parabola.Solve(paraA, paraB, paraC, t) - Parabola.Solve(paraA, paraB, paraC, 0.0f),
                            t * distance);
                        rotation.Value = quaternion.Euler(altitudeAngle, 0, 0);
                    }
                }
            }
Пример #8
0
            public void Execute(ref Translation position, ref ArcState arc)
            {
                float t = ElapsedTime - arc.StartTime;

                if (t > arc.Duration)
                {
                    // Compute the next arc info
                    int2 playerCoords = TerrainSystem.PositionToBlockCoords(position.Value.x, position.Value.z, TerrainSize);
                    int2 dir          = math.clamp(MouseBoxCoords - playerCoords,
                                                   new int2(-1, -1), new int2(1, 1));
                    int2 destCoords = playerCoords + dir;
                    // If destination is outside the terrain bounds, don't move.
                    if (destCoords.x < 0 || destCoords.x >= TerrainSize.x ||
                        destCoords.y < 0 || destCoords.y >= TerrainSize.y)
                    {
                        destCoords = playerCoords;
                    }
                    int playerBlockIndex = TerrainSystem.BlockCoordsToIndex(playerCoords, TerrainSize);
                    int destBlockIndex   = TerrainSystem.BlockCoordsToIndex(destCoords, TerrainSize);
                    // If destination is occupied, don't move.
                    if (IsBlockOccupied[destBlockIndex] != 0)
                    {
                        destCoords     = playerCoords;
                        destBlockIndex = playerBlockIndex;
                    }

                    float playerBlockHeight = BlockHeights[playerBlockIndex];
                    float destBlockHeight   = BlockHeights[destBlockIndex];
                    float parabolaHeight    = math.max(playerBlockHeight, destBlockHeight);
                    if (dir.x != 0 && dir.y != 0)
                    {
                        // If jumping diagonally, the parabola height must take into account the directly
                        // adjacent blocks as well.
                        int2 adjacentCoordsX = playerCoords + new int2(dir.x, 0);
                        if (adjacentCoordsX.x >= 0 && adjacentCoordsX.x < TerrainSize.x)
                        {
                            int   adjacentIndexX  = TerrainSystem.BlockCoordsToIndex(adjacentCoordsX, TerrainSize);
                            float adjacentHeightX = BlockHeights[adjacentIndexX];
                            parabolaHeight = math.max(parabolaHeight, adjacentHeightX);
                        }
                        int2 adjacentCoordsZ = playerCoords + new int2(0, dir.y);
                        if (adjacentCoordsZ.y >= 0 && adjacentCoordsZ.y < TerrainSize.y)
                        {
                            int   adjacentIndexZ  = TerrainSystem.BlockCoordsToIndex(adjacentCoordsZ, TerrainSize);
                            float adjacentHeightZ = BlockHeights[adjacentIndexZ];
                            parabolaHeight = math.max(parabolaHeight, adjacentHeightZ);
                        }
                    }
                    parabolaHeight += BOUNCE_HEIGHT;

                    Parabola.Create(playerBlockHeight, parabolaHeight, destBlockHeight,
                                    out arc.Parabola.x, out arc.Parabola.y, out arc.Parabola.z);
                    arc.StartPos =
                        TerrainSystem.BlockCoordsToPosition(playerCoords, playerBlockHeight, TerrainSize);
                    arc.EndPos =
                        TerrainSystem.BlockCoordsToPosition(destCoords, destBlockHeight, TerrainSize);
                    arc.StartTime = ElapsedTime;
                    float distance = math.distance(playerCoords, destCoords);
                    arc.Duration = math.max(1, distance) * BOUNCE_BASE_DURATION;
                }
                else
                {
                    // Get the current height along the parabola
                    float s = t / arc.Duration;
                    position.Value = new float3(
                        math.lerp(arc.StartPos.x, arc.EndPos.x, s),
                        Parabola.Solve(arc.Parabola.x, arc.Parabola.y, arc.Parabola.z, s),
                        math.lerp(arc.StartPos.z, arc.EndPos.z, s)
                        );
                }
            }