private bool TestTileCollision(World world, AABB a, AABB b, Vector2 delta, ref float tMin, ref Vector2 normal) { bool result = false; b.Expand(a.radius); a.Shrink(a.radius); Vector2Int tPos = Utils.TilePos(b.center); Vector2 wMin = b.center - b.radius, wMax = b.center + b.radius; Tile up = world.GetTile(tPos.x, tPos.y + 1); Tile down = world.GetTile(tPos.x, tPos.y - 1); Tile left = world.GetTile(tPos.x - 1, tPos.y); Tile right = world.GetTile(tPos.x + 1, tPos.y); // Top surface. if (TileManager.GetData(up).passable&& TestWall(delta, a.center, wMax.y, wMin, wMax, 1, 0, ref tMin)) { normal = Vector2.up; if (delta.y < 0.0f) { colFlags |= CollideFlags.Below; result = true; } } // Bottom surface. if (TileManager.GetData(down).passable&& TestWall(delta, a.center, wMin.y, wMin, wMax, 1, 0, ref tMin)) { normal = Vector2.down; colFlags |= CollideFlags.Above; result = true; } // Left wall. if (TileManager.GetData(left).passable&& TestWall(delta, a.center, wMin.x, wMin, wMax, 0, 1, ref tMin)) { normal = Vector2.left; colFlags |= CollideFlags.Left; result = true; } // Right wall. if (TileManager.GetData(right).passable&& TestWall(delta, a.center, wMax.x, wMin, wMax, 0, 1, ref tMin)) { normal = Vector2.right; colFlags |= CollideFlags.Right; result = true; } return(result); }
public void Move(Vector2 accel, float gravity) { if (disabled) { return; } facingLockTime -= Time.deltaTime; KillOnOverlap(); moveState = MoveState.Normal; accel *= speed; accel += velocity * friction; if (gravity != 0.0f) { accel.y = gravity; } // Using the following equations of motion: // - p' = 1/2at^2 + vt + p. // - v' = at + v. // - a = specified by input. // Where a = acceleration, v = velocity, and p = position. // v' and p' denote new versions, while non-prime denotes old. // These are found by integrating up from acceleration to velocity. Use derivation // to go from position down to velocity and then down to acceleration to see how // we can integrate back up. Vector2 delta = accel * 0.5f * Utils.Square(Time.deltaTime) + velocity * Time.deltaTime; if (swimming) { velocity = (accel * 0.35f) * Time.deltaTime + velocity; } else { velocity = accel * Time.deltaTime + velocity; } Vector2 target = Position + delta; AABB entityBB = GetBoundingBox(); colFlags = CollideFlags.None; // Player size in tiles. Vector2Int tSize = Utils.CeilToInt(entityBB.radius * 2.0f); Vector2Int start = Utils.TilePos(Position); Vector2Int end = Utils.TilePos(target); // Compute the range of tiles we could touch with our movement. We'll test for collisions // with the tiles in this range. Vector2Int min = new Vector2Int(Mathf.Min(start.x, end.x) - tSize.x, Mathf.Min(start.y, end.y) - tSize.y); Vector2Int max = new Vector2Int(Mathf.Max(start.x, end.x) + tSize.x, Mathf.Max(start.y, end.y) + tSize.y); // Tile collision checking. GetPossibleCollidingTiles(world, entityBB, min, max); // Entity collision checking. GetPossibleCollidingEntities(world, entityBB, min, max); possibleCollides.Sort(collideCompare); float tRemaining = 1.0f; for (int it = 0; it < 3 && tRemaining > 0.0f; ++it) { float tMin = 1.0f; Vector2 normal = Vector2.zero; CollideResult hitResult = default; bool hit = false; for (int i = 0; i < possibleCollides.Count; ++i) { CollideResult info = possibleCollides[i]; bool result = TestTileCollision(world, entityBB, info.bb, delta, ref tMin, ref normal); if (result) { hitResult = info; hit = true; } } MoveBy(delta * tMin); if (normal != Vector2.zero) { MoveBy((normal * Epsilon)); } if (hit) { OnCollide(hitResult); } entityBB = GetBoundingBox(); // Subtract away the component of the velocity that collides with the tile wall // and leave the remaining velocity intact. velocity -= Vector2.Dot(velocity, normal) * normal; delta -= Vector2.Dot(delta, normal) * normal; delta -= (delta * tMin); tRemaining -= (tMin * tRemaining); } possibleCollides.Clear(); if (overlaps.Count > 0) { HandleOverlaps(overlaps); } overlaps.Clear(); SetFacingDirection(); Rebase(world); ApplyOverTimeDamage(); if (DebugServices.Instance.ShowDebug) { GetBoundingBox().Draw(Color.green); } }