// Update is called once per frame void Update() { if (Game.instance.isPaused) { return; } UpdateTranslation(); RotateTowardsPlayer(); time += Time.deltaTime; if (time >= Game.instance.tankLaunchPeriod) { // launching cannonball time -= Game.instance.tankLaunchPeriod; // start and end positions Vector3 start = transform.localPosition; Vector2Int playerBox = TerrainArea.instance.BoxFromLocalPosition(Player.instance.transform.localPosition); Vector3 end = TerrainArea.instance.LocalPositionFromBox(playerBox.x, playerBox.y, TerrainArea.instance.GetBox(playerBox.x, playerBox.y).top + Cannonball.RADIUS); float distance = (new Vector2(end.z - start.z, end.x - start.x)).magnitude; float duration = distance / Cannonball.SPEED; if (duration < .0001f) { duration = 1; } // binary searching to determine height of cannonball arc float low = Mathf.Max(start.y, end.y); float high = low * 2; float paraA, paraB, paraC; // look for height of arc that won't hit boxes while (true) { Parabola.Create(start.y, high, end.y, out paraA, out paraB, out paraC); if (!Cannonball.CheckBoxCollision(start, end, 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 > Game.instance.playerParabolaPrecision) { float mid = (low + high) / 2; Parabola.Create(start.y, mid, end.y, out paraA, out paraB, out paraC); if (Cannonball.CheckBoxCollision(start, end, paraA, paraB, paraC)) { // not high enough low = mid; } else { // too high high = mid; } } // launch with calculated height float height = (low + high) / 2; Cannonball cannonball = Cannonball.Create(transform.parent, start); cannonball.Launch(end, height, duration); // set cannon rotation SetCannonRotation(Mathf.Atan2(Parabola.Solve(paraA, paraB, paraC, .1f) - Parabola.Solve(paraA, paraB, paraC, 0), .1f) * Mathf.Rad2Deg); } }
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) ); } }