protected override void DoCheck(IMovable user, ref Vector2 vel, ref CollisionStatus collStatus, LayerMask mask, Bounds2D bounds, Bounds2D shrinkedBounds) { //Direction var direction = Math.Sign(vel.y); var verticalRays = user.VerticalRaycasts; var directionVector = new Vector2(0, vel.y * Time.deltaTime); // Bounds2D var bMin = bounds.Min; var bMax = bounds.Max; var positiveDir = direction == 1; var originX = shrinkedBounds.Min.x; var originY = positiveDir ? bMax.y : bMin.y; var origin = new Vector2(originX, originY); var width = shrinkedBounds.Size.x; var spacing = width / (verticalRays - 1); var rayLength = directionVector.magnitude; LastHit = null; var hitDown = false; for (byte x = 0; x < verticalRays; x++) { var raycast = Physics2D.Raycast(origin, directionVector, rayLength, mask); Debug.DrawRay(origin, directionVector, raycast ? Color.green : Color.red); if (raycast && !raycast.collider.isTrigger && raycast.distance < rayLength) { LastHit = raycast; vel.y = raycast.distance / Time.deltaTime * direction; rayLength = raycast.distance; collStatus.Down = direction == -1; collStatus.Up = direction == 1; if (!hitDown) { hitDown = direction == -1; } } origin.x += spacing; } if (!hitDown) { collStatus.Down = false; } }
public override void Move(IMovable user, ref Vector2 velocity, ref CollisionStatus collisionStatus, StateMotorMachine machine, StateMotorConfig config1, LayerMask collisionMask) { var config = user.GetMotorConfig <GroundMotorConfig>(); if (config == null) { UPMDebug.LogWarning("Expected GroundMotorConfig for GroundedState @ " + user); return; } ProcessInputs(user, config, ref velocity, ref collisionStatus); velocity.y += Gravity * config.GravityScale * Time.deltaTime; var max = user.MaxSpeed; velocity.x = Mathf.Clamp(velocity.x, -max, max); GroundedBehaviour.Check(user, ref velocity, ref collisionStatus, collisionMask); }
public CollisionInformations(CollisionStatus status, Box obj, Box target, float tc, Vector3 x0p, Vector3 x1p, Vector3 p0p, Vector3 p1p, Vector3 speed0, Vector3 speed1, Vector3 speed20, Vector3 speed21, Vector3 av, Vector3 aw) { collision_type = status; colliding_obj = obj; target_obj = target; t = tc; suggested_x0 = x0p; suggested_x1 = x1p; suggested_p0 = p0p; suggested_p1 = p1p; suggested_av = av; suggested_aw = aw; suggested_speed0 = speed0; suggested_speed1 = speed1; suggested_speed20 = speed20; suggested_speed21 = speed21; }
public override void Move(IMovable user, ref Vector2 velocity, ref CollisionStatus status) { var config = user.GetMotorConfig <StateMotorConfig>(); if (config == null) { UPMDebug.LogWarning("Expected user " + user + " to have a StateMotorConfig but found none"); return; } var m = config.StateMachine; if (m == null) { UPMDebug.LogError("Expected user " + user + " to have a state machine but found none"); return; } m.Move(user, ref velocity, ref status, config, CollisionMask); }
private CollisionInformations() { collision_type = CollisionStatus.NO_COLLISION; suggested_speed0 = Vector3.Zero; suggested_speed1 = Vector3.Zero; suggested_x0 = Vector3.Zero; suggested_x1 = Vector3.Zero; suggested_speed20 = Vector3.Zero; suggested_speed21 = Vector3.Zero; suggested_p0 = Vector3.Zero; suggested_p1 = Vector3.Zero; suggested_av = Vector3.Zero; suggested_aw = Vector3.Zero; colliding_obj = null; target_obj = null; t = 1; // whole deltaT passed }
public CollisionInformations(CollisionStatus status, Vector3 x0p, Vector3 x1p, Vector3 spd1) { collision_type = status; suggested_speed0 = spd1; suggested_speed1 = spd1; suggested_x0 = x0p; suggested_x1 = x1p; suggested_speed20 = Vector3.Zero; suggested_speed21 = Vector3.Zero; suggested_p0 = Vector3.Zero; suggested_p1 = Vector3.Zero; suggested_av = Vector3.Zero; suggested_aw = Vector3.Zero; colliding_obj = null; target_obj = null; t = 1; }
public CollisionInformations(CollisionStatus status, Vector3 x0p, Vector3 x1p, Vector3 spd1, Box obj, Box target, float tc) { collision_type = status; suggested_speed0 = spd1; suggested_speed1 = spd1; suggested_x0 = x0p; suggested_x1 = x1p; suggested_speed20 = Vector3.Zero; suggested_speed21 = Vector3.Zero; suggested_p0 = Vector3.Zero; suggested_p1 = Vector3.Zero; suggested_av = Vector3.Zero; suggested_aw = Vector3.Zero; colliding_obj = obj; target_obj = target; t = tc; }
public CollisionInformations(Vector3 x0p, Vector3 x1p, Vector3 spd1, float tc) { collision_type = CollisionStatus.COLLISION_FIXED; suggested_speed0 = spd1; suggested_speed1 = spd1; suggested_x0 = x0p; suggested_x1 = x1p; suggested_speed20 = Vector3.Zero; suggested_speed21 = Vector3.Zero; suggested_p0 = Vector3.Zero; suggested_p1 = Vector3.Zero; suggested_av = Vector3.Zero; suggested_aw = Vector3.Zero; colliding_obj = null; target_obj = null; t = tc; }
// this method exists so that derived classes can decide what changes can be made // to itself just before a collision happen, like for example, // removing bananas as the monkey collects them protected virtual CollisionStatus BeforeCollision(Collidable <TBoard> otherCollidable, CollisionStatus direction) { // this is meant for overriding // but by default, there should be no effect // if we do not want CollisionAfter to be invoked after this method, simply set direction to // no collision return(direction); }
public CollisionEventArgs(Collidable <TBoard> otherCollidable, CollisionStatus direction) { Direction = direction; OtherCollidable = otherCollidable; }
protected override CollisionStatus BeforeCollision(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { // display some debug message if (otherCollidable is Monkey) { // this will get board to remove positional, unbind its tick from given positional, and emit an event to boardUI to remove other aspects eatSound.Play(); // this works because MonkeyBanana must take in only MonkeyBoard Board.Dispatcher.Invoke((Action)(() => Board.IncrementBananaCollected())); Board.RemovePositional(this); return(CollisionStatus.NoCollision); } return(direction); }
// Use this for initialization void Start() { status = new CollisionStatus(); status.Setup(); }
private static void FireBullets(MutableGameState gameState, TankAction[] tankActions, bool[] bulletsThatMovedThisTurn, IList <Point> wallsRemoved) { bool[] bulletWasFired = new bool[Constants.TANK_COUNT]; Point[] wallsShot = new Point[MAX_WALLS_SHOT]; byte wallsShotCount = 0; #if DEBUG CollisionStatus[] collisionStatuses = new CollisionStatus[Constants.ALL_ELEMENT_COUNT]; #endif for (int t = 0; t < tankActions.Length; t++) { TankAction tankAction = tankActions[t]; if (tankAction != TankAction.FIRE) { continue; } MobileState tankState = gameState.MobileStates[t]; if (!tankState.IsActive) { continue; } // A bullet can't be fired if there is already an active bullet from the same tank: int bulletIndex = t + Constants.TANK_COUNT; MobileState bulletState = gameState.MobileStates[bulletIndex]; if (bulletState.IsActive) { continue; } // If a bullet was active this turn but was destroyed, // then the tank probably still can't fire another bullet yet: // UPDATE: It seems that the harness does allow this... if (bulletsThatMovedThisTurn[t] && !GameRuleConfiguration.RuleConfiguration.CanATankFireAgainOnTheSameTurnThatItsBulletWasDestroyed) { continue; } bulletState = tankState.FireABulletAndGetItsState(); // A bullet can't be shot outside the board: if (bulletState.Pos.X < 0 || bulletState.Pos.Y < 0 || bulletState.Pos.X >= Game.Current.BoardWidth || bulletState.Pos.Y >= Game.Current.BoardHeight) { #if DEBUG collisionStatuses[bulletIndex] |= CollisionStatus.WithOutOfBoundsArea; #endif bulletState = bulletState.Kill(); gameState.MobileStates[bulletIndex] = bulletState; continue; } // Check if there is a wall to shoot: if (!gameState.Walls[bulletState.Pos]) { gameState.MobileStates[bulletIndex] = bulletState; bulletWasFired[t] = true; } else { bulletState = bulletState.Kill(); gameState.MobileStates[bulletIndex] = bulletState; /* Shoot out the walls on the segment. * Assume that they are all in bounds since the tank could not have shot the bullet otherwise * TODO: What to do when the board shrinks after the time limit is up, * as then part of the segment could be out of bounds? */ int first; int last; int x; int y; switch (bulletState.Dir) { case Direction.LEFT: case Direction.RIGHT: x = bulletState.Pos.X; first = bulletState.Pos.Y - 2; last = bulletState.Pos.Y + 2; for (y = first; y <= last; y++) { if (gameState.Walls[x, y]) { wallsShot[wallsShotCount] = new Point((short)x, (short)y); wallsShotCount++; } } break; case Direction.DOWN: case Direction.UP: y = bulletState.Pos.Y; first = bulletState.Pos.X - 2; last = bulletState.Pos.X + 2; for (x = first; x <= last; x++) { if (gameState.Walls[x, y]) { wallsShot[wallsShotCount] = new Point((short)x, (short)y); wallsShotCount++; } } break; } } } // Check for collisions between bullets, tanks and bases: for (int t = 0; t < Constants.TANK_COUNT; t++) { if (!bulletWasFired[t]) { continue; } int b = t + Constants.TANK_COUNT; MobileState bulletState = gameState.MobileStates[b]; if (!bulletState.IsActive) { continue; } // Check for collisions with other bullets, tanks and bases: for (int i = 0; i < Constants.MOBILE_ELEMENT_COUNT; i++) { if (i == b) { continue; } MobileState otherState = gameState.MobileStates[i]; if (otherState.IsActive) { if (i < Constants.TANK_COUNT) { // Check for collision with a tank: if (otherState.GetTankExtent().ContainsPoint(bulletState.Pos)) { #if DEBUG collisionStatuses[i] |= CollisionStatus.WithBullet; collisionStatuses[b] |= CollisionStatus.WithTank; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.MobileStates[i] = otherState.Kill(); } } else { // Check for collision with another bullet: if (otherState.Pos == bulletState.Pos) { #if DEBUG collisionStatuses[b] |= CollisionStatus.WithBullet; collisionStatuses[i] |= CollisionStatus.WithBullet; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.MobileStates[i] = otherState.Kill(); } } } } // Check for collisions with bases: for (int p = 0; p < Constants.PLAYERS_PER_GAME; p++) { Base @base = Game.Current.Players[p].Base; if (bulletState.Pos == @base.Pos) { #if DEBUG collisionStatuses[b] |= CollisionStatus.WithBase; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.Outcome |= (Outcome)(((byte)Outcome.Player1BaseKilled) << p); } } } // Remove walls that were shot: for (int w = 0; w < wallsShotCount; w++) { if (wallsRemoved == null) { gameState.Walls[wallsShot[w]] = false; } else { Point wall = wallsShot[w]; if (gameState.Walls[wall]) { gameState.Walls[wall] = false; wallsRemoved.Add(wall); } } } }
public abstract void Move(IMovable user, ref Vector2 velocity, ref CollisionStatus status);
protected override void CollisionAfter(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { if (otherCollidable is Crate) { if (direction == CollisionStatus.CollisionFromLeft || direction == CollisionStatus.CollisionFromRight) { LastCrateCollisionDirection = direction; } else if (direction == CollisionStatus.CollisionFromTop) { Board.TempRemovePositional(this); if (LastCrateCollisionDirection == CollisionStatus.CollisionFromLeft) { SetPosition(otherCollidable.X + 1, Y, 0, 0); } else { SetPosition(otherCollidable.X - 1, Y, 0, 0); } Board.RestorePositional(this); Board.Dispatcher.Invoke((Action)(() => Board.DecreaseNumberOfLives())); } } }
protected abstract void DoCheck(IMovable user, ref Vector2 vel, ref CollisionStatus collStatus, LayerMask mask, Bounds2D bounds, Bounds2D shrinkedBounds);
protected override void CollisionAfter(Collidable <TBoard> otherCollidable, CollisionStatus direction) { OnColliding(new CollisionEventArgs <TBoard>(otherCollidable, direction)); base.CollisionAfter(otherCollidable, direction); }
protected override void CollisionAfter(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { if (otherCollidable is Monkey && !IsMonkeyAtDoor) { // culprit // can't fade out here and don't know why // should be something to do with threading // can't go past stage 2 also Board.Dispatcher.Invoke((Action)(() => { Board.OnBoardFadeOut(this); IsMonkeyAtDoor = true; })); } }
protected override void CollisionAfter(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { if (otherCollidable is Monkey) { Monkey monkey = otherCollidable as Monkey; if (!IsCurrentlySliding() && monkey.IsOnGround()) { if (direction == CollisionStatus.CollisionFromLeft && monkey.IsTryingToMoveRight || direction == CollisionStatus.CollisionFromRight && monkey.IsTryingToMoveLeft) { if (!StartedPush) { StartedPush = true; //Debug.WriteLine("started push"); } else if (PushTick == DelayTicks) { // this means that there is enough "push" on the crate // and it should start moving if (direction == CollisionStatus.CollisionFromLeft) { SlideRight(); } else { SlideLeft(); } // resets push tick StartedPush = false; PushTick = 0; soundPlayer.Play(); } } } } }
protected override void CollisionAfter(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { }
// Update position AGV icon in simulation mode (speed: cm/s) public static Point SimUpdatePositionAGV(int agvID, float speed) { // Find AGV in SimListAGV, get current point var index = AGV.SimListAGV.FindIndex(a => a.ID == agvID); AGV agv = AGV.SimListAGV[index]; Point position = Display.SimLabelAGV[agvID].Location; // Handle (waiting method) collision if it happens CollisionStatus status = Collision.SimHandle(agv, Collision.SimListCollision); if (status == CollisionStatus.Handling) { // Update agv status and velocity agv.Velocity = 0; agv.Status = "Stop"; return(position); } // return old point when agv has no path if (agv.Path.Count == 0) { return(position); } // Get navigation frame array. Note: string is a reference type, // so any change in navigationArr is also in AGV.SimListAGV[index].navigationArr string[] navigationArr = agv.navigationArr; // Check whether current point is a node or not // Note: shift position of label to center (+LabelAGV[].Width/2, +LabelAGV[].Height/2) var node = Node.ListNode.FirstOrDefault(n => { return((n.X == position.X + SimLabelAGV[agvID].Width / 2) && (n.Y == position.Y + SimLabelAGV[agvID].Height / 2)); }); // Current point is not a node and current position is start node, // it means initDistance to start node != 0, so go backward once then keep go ahead char orient = new char(); if (node == null && agv.ExitNode == agv.Path[0]) { switch (navigationArr[0]) { case "A": orient = UpdateOrient(agv.Orientation, "A"); break; case "B": orient = UpdateOrient(agv.Orientation, "B"); navigationArr[0] = "A"; break; } } // Current point is not a node and current position is not start node, // so keep go ahead else if (node == null) { orient = UpdateOrient(agv.Orientation, "A"); } // If goal was reached, no update position, remove old path, get next path (if exist) else if (node.ID == agv.Path.LastOrDefault()) { // Update AGV information agv.ExitNode = node.ID; // Update ExitNode orient = UpdateOrient(agv.Orientation, "A"); agv.Orientation = orient; // Update Orientation agv.DistanceToExitNode = 0f; // Update Distance to ExitNode agv.Status = "Stop"; // Update Status agv.Velocity = 0; // Update Velocity // Add next path Task.AddNextPathOfSimAGV(agv); return(position); } // Current point is at start node and initDistance to start node == 0 // Turn direction once then keep go ahead else if (node.ID == agv.Path[0] && agv.DistanceToExitNode == 0f) { switch (navigationArr[0]) { case "A": orient = UpdateOrient(agv.Orientation, "A"); break; case "B": orient = UpdateOrient(agv.Orientation, "B"); navigationArr[0] = "A"; break; case "L": orient = UpdateOrient(agv.Orientation, "L"); navigationArr[0] = "A"; break; case "R": orient = UpdateOrient(agv.Orientation, "R"); navigationArr[0] = "A"; break; } // If this node is pick node, remove pallet code that was picked and save this time if (agv.Tasks.Count != 0 && node.ID == agv.Tasks[0].PickNode) { RackColumn column = RackColumn.SimListColumn.Find(c => c.AtNode == agv.ExitNode); Pallet.SaveDeliveryTime(column.PalletCodes[agv.Tasks[0].PickLevel - 1], Pallet.SimListPallet); DBUtility.UpdatePalletDB("SimPalletInfoTable", column.PalletCodes[agv.Tasks[0].PickLevel - 1], false, DateTime.Now.ToString("dddd, MMMM dd, yyyy h:mm:ss tt"), Pallet.SimListPallet); column.PalletCodes[agv.Tasks[0].PickLevel - 1] = null; } } // Current point is a node but start node else { int idx = Array.IndexOf(navigationArr, node.ID.ToString()); string dir = navigationArr[idx + 1]; orient = UpdateOrient(agv.Orientation, dir); } // Modify speed to make sure AGV icon can reach next node int i = agv.Path.IndexOf(agv.ExitNode); int nextNode = agv.Path[i + 1]; int dnx = (position.X + SimLabelAGV[agvID].Width / 2) - Node.ListNode[nextNode].X; int dny = (position.Y + SimLabelAGV[agvID].Height / 2) - Node.ListNode[nextNode].Y; int nd = (int)Math.Sqrt(dnx * dnx + dny * dny); if (agv.ExitNode == agv.Path[0]) { // At first node of path, having 4 cases of agv position // (dnx * dny != 0) for 2 cases and (dnx * dnx0 > 0 || dny * dny0 > 0) for the others int dnx0 = (position.X + SimLabelAGV[agvID].Width / 2) - Node.ListNode[agv.Path[0]].X; int dny0 = (position.Y + SimLabelAGV[agvID].Height / 2) - Node.ListNode[agv.Path[0]].Y; int nd0 = (int)Math.Sqrt(dnx0 * dnx0 + dny0 * dny0); if (dnx * dny != 0 || dnx * dnx0 > 0 || dny * dny0 > 0) { nextNode = agv.Path[0]; nd = nd0; } } int sp = (int)Math.Round(speed * Display.Scale * (100.0 / 1000)); // timer1.Interval = 100ms int step = (nd % sp == 0) ? sp : (nd % sp); // Update AGV information before update position if (node != null) { agv.ExitNode = node.ID; // Update ExitNode } agv.Orientation = orient; // Update Orientation int exitNode = agv.ExitNode; int dx = (position.X + SimLabelAGV[agvID].Width / 2) - Node.ListNode[exitNode].X; int dy = (position.Y + SimLabelAGV[agvID].Height / 2) - Node.ListNode[exitNode].Y; agv.DistanceToExitNode = (float)Math.Sqrt(dx * dx + dy * dy) / Display.Scale; // Update Distance to ExitNode agv.Status = "Running"; // Update Status // Update next position of AGV icon in panel switch (orient) { case 'E': position = new Point(position.X + step, position.Y); break; case 'W': position = new Point(position.X - step, position.Y); break; case 'S': position = new Point(position.X, position.Y + step); break; case 'N': position = new Point(position.X, position.Y - step); break; } return(position); }
public void Move(IMovable user, ref Vector2 velocity, ref CollisionStatus status, StateMotorConfig config, LayerMask collisionMask) { state.Move(user, ref velocity, ref status, this, config, collisionMask); }
protected override void CollisionAfter(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { if (otherCollidable is AI <MonkeyBoard> ) { AIKilledSound.Play(); Board.RemovePositional(otherCollidable); Board.RemovePositional(this); } if (otherCollidable is BorderTile) { Board.RemovePositional(this); } }
protected override void CollisionAfter(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { if (!(otherCollidable is Monkey) && !(otherCollidable is BasicWeapon) && otherCollidable is Collidable <MonkeyBoard> ) { RemoveMovement(PreviousMovement); if (direction == CollisionStatus.CollisionFromRight) { PreviousMovement = new SlideLeftRight(Board.CalculateFineness(-MovementSpeedProportion)); } else if (direction == CollisionStatus.CollisionFromLeft) { PreviousMovement = new SlideLeftRight(Board.CalculateFineness(MovementSpeedProportion)); } // changes the direction to reverse AddMovement(PreviousMovement); } base.CollisionAfter(otherCollidable, direction); }
// this method exists so that derived classes can decide what changes can be made // to itself after a collision with another collidable object protected virtual void CollisionAfter(Collidable <TBoard> otherCollidable, CollisionStatus direction) { // this is meant for overriding // but by default, there should be no effect }
protected override CollisionStatus BeforeCollision(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { if (otherCollidable is Monkey) { if (direction == CollisionStatus.CollisionFromTop) { return(CollisionStatus.CollisionFromTop); } else { Board.RemovePositional(otherCollidable); if (MonkeyKilledSound != null) { MonkeyKilledSound.Play(); } Board.Dispatcher.Invoke((Action)(() => Board.DecreaseNumberOfLives())); return(CollisionStatus.NoCollision); } } return(base.BeforeCollision(otherCollidable, direction)); }
private static void MoveTanks(MutableGameState gameState, TankAction[] tankActions) { #if DEBUG CollisionStatus[] collisionStatuses = new CollisionStatus[Constants.ALL_ELEMENT_COUNT]; #endif foreach (int t in GameRuleConfiguration.RuleConfiguration.TankMovementIndexes) { MobileState tankState = gameState.MobileStates[t]; if (!tankState.IsActive) { continue; } bool canMove = true; bool willTurn = true; bool willDie = false; TankAction tankAction = tankActions[t]; Direction movementDir = Direction.NONE; int firstSeg; int lastSeg; int segX = 0; int segY = 0; int newCentreX = tankState.Pos.X; int newCentreY = tankState.Pos.Y; switch (tankAction) { case TankAction.UP: movementDir = Direction.UP; willTurn = (movementDir != tankState.Dir); if (tankState.Pos.Y <= Constants.TANK_EXTENT_OFFSET) { // Trying to move off the edge of the board: #if DEBUG collisionStatuses[t] |= CollisionStatus.WithOutOfBoundsArea; #endif canMove = false; willDie = GameRuleConfiguration.RuleConfiguration.DoesATankDieIfTryingToMoveOffTheBoard; willTurn = willTurn && GameRuleConfiguration.RuleConfiguration.DoesATankTurnIfTryingToMoveOffTheBoard; } else { newCentreY--; segY = newCentreY - Constants.TANK_EXTENT_OFFSET; } break; case TankAction.DOWN: movementDir = Direction.DOWN; willTurn = (movementDir != tankState.Dir); if (tankState.Pos.Y + Constants.TANK_OUTER_EDGE_OFFSET >= Game.Current.BoardHeight) { // Trying to move off the edge of the board: #if DEBUG collisionStatuses[t] |= CollisionStatus.WithOutOfBoundsArea; #endif canMove = false; willDie = GameRuleConfiguration.RuleConfiguration.DoesATankDieIfTryingToMoveOffTheBoard; willTurn = willTurn && GameRuleConfiguration.RuleConfiguration.DoesATankTurnIfTryingToMoveOffTheBoard; } else { newCentreY++; segY = newCentreY + Constants.TANK_EXTENT_OFFSET; } break; case TankAction.LEFT: movementDir = Direction.LEFT; willTurn = (movementDir != tankState.Dir); if (tankState.Pos.X <= Constants.TANK_EXTENT_OFFSET) { // Trying to move off the edge of the board: #if DEBUG collisionStatuses[t] |= CollisionStatus.WithOutOfBoundsArea; #endif canMove = false; willDie = GameRuleConfiguration.RuleConfiguration.DoesATankDieIfTryingToMoveOffTheBoard; willTurn = willTurn && GameRuleConfiguration.RuleConfiguration.DoesATankTurnIfTryingToMoveOffTheBoard; } else { newCentreX--; segX = newCentreX - Constants.TANK_EXTENT_OFFSET; } break; case TankAction.RIGHT: movementDir = Direction.RIGHT; willTurn = (movementDir != tankState.Dir); if (tankState.Pos.X + Constants.TANK_OUTER_EDGE_OFFSET >= Game.Current.BoardWidth) { // Trying to move off the edge of the board: #if DEBUG collisionStatuses[t] |= CollisionStatus.WithOutOfBoundsArea; #endif canMove = false; willDie = GameRuleConfiguration.RuleConfiguration.DoesATankDieIfTryingToMoveOffTheBoard; willTurn = willTurn && GameRuleConfiguration.RuleConfiguration.DoesATankTurnIfTryingToMoveOffTheBoard; } else { newCentreX++; segX = newCentreX + Constants.TANK_EXTENT_OFFSET; } break; default: // case TankAction.FIRE: // case TankAction.NONE: canMove = false; willTurn = false; break; } if (canMove) { switch (movementDir) { case Direction.DOWN: case Direction.UP: firstSeg = newCentreX - Constants.TANK_EXTENT_OFFSET; lastSeg = newCentreX + Constants.TANK_EXTENT_OFFSET; for (segX = firstSeg; segX <= lastSeg; segX++) { if (gameState.Walls[segX, segY]) { canMove = false; break; } } if (canMove) { Rectangle newRect = new Rectangle( (short)(newCentreX - Constants.TANK_EXTENT_OFFSET), (short)(newCentreY - Constants.TANK_EXTENT_OFFSET), (short)(newCentreX + Constants.TANK_EXTENT_OFFSET), (short)(newCentreY + Constants.TANK_EXTENT_OFFSET)); // Check if moving into another tank: for (int tOther = 0; tOther < Constants.TANK_COUNT; tOther++) { if (tOther != t) { MobileState otherTankState = gameState.MobileStates[tOther]; if (otherTankState.IsActive && otherTankState.GetTankExtent().IntersectsWith(newRect)) { canMove = false; break; } } } if (canMove) { // Check if moving into a bullet: for (int b = Constants.MIN_BULLET_INDEX; b <= Constants.MAX_BULLET_INDEX; b++) { MobileState bulletState = gameState.MobileStates[b]; if (bulletState.IsActive && (bulletState.Pos.Y == segY)) { for (segX = firstSeg; segX <= lastSeg; segX++) { if (bulletState.Pos.X == segX) { willDie = GameRuleConfiguration.RuleConfiguration.DoesATankDieIfMovingIntoABullet; #if DEBUG collisionStatuses[t] |= CollisionStatus.WithBullet; #endif if (willDie) { #if DEBUG collisionStatuses[b] |= CollisionStatus.MovedIntoByATank; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; } canMove = !willDie; break; } } if (willDie || !canMove) { break; } } } } } break; case Direction.LEFT: case Direction.RIGHT: firstSeg = newCentreY - Constants.TANK_EXTENT_OFFSET; lastSeg = newCentreY + Constants.TANK_EXTENT_OFFSET; for (segY = firstSeg; segY <= lastSeg; segY++) { if (gameState.Walls[segX, segY]) { canMove = false; break; } } if (canMove) { Rectangle newRect = new Rectangle( (short)(newCentreX - Constants.TANK_EXTENT_OFFSET), (short)(newCentreY - Constants.TANK_EXTENT_OFFSET), (short)(newCentreX + Constants.TANK_EXTENT_OFFSET), (short)(newCentreY + Constants.TANK_EXTENT_OFFSET)); // Check if moving into another tank: for (int tOther = 0; tOther < Constants.TANK_COUNT; tOther++) { if (tOther != t) { MobileState otherTankState = gameState.MobileStates[tOther]; if (otherTankState.IsActive && otherTankState.GetTankExtent().IntersectsWith(newRect)) { canMove = false; break; } } } if (canMove) { // Check if moving into a bullet: for (int b = Constants.MIN_BULLET_INDEX; b <= Constants.MAX_BULLET_INDEX; b++) { MobileState bulletState = gameState.MobileStates[b]; if (bulletState.IsActive && (bulletState.Pos.X == segX)) { for (segY = firstSeg; segY <= lastSeg; segY++) { if (bulletState.Pos.Y == segY) { willDie = GameRuleConfiguration.RuleConfiguration.DoesATankDieIfMovingIntoABullet; #if DEBUG collisionStatuses[t] |= CollisionStatus.WithBullet; #endif if (willDie) { #if DEBUG collisionStatuses[b] |= CollisionStatus.MovedIntoByATank; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; } canMove = !willDie; break; } } if (willDie || !canMove) { break; } } } } } break; } } if (canMove || willTurn || willDie) { if (canMove) { tankState = tankState.MoveTo(newCentreX, newCentreY); } if (willDie) { tankState = tankState.Kill(); } if (willTurn) { tankState = tankState.ChangeDirection(movementDir); } gameState.MobileStates[t] = tankState; } } // Check for collisions with bases: for (int b = Constants.MIN_BULLET_INDEX; b <= Constants.MAX_BULLET_INDEX; b++) { MobileState bulletState = gameState.MobileStates[b]; if (bulletState.IsActive) { for (int i = 0; i < Constants.MOBILE_ELEMENT_COUNT; i++) { if (i == b) { continue; } MobileState otherState = gameState.MobileStates[i]; if (!otherState.IsActive) { continue; } if (i < Constants.TANK_COUNT) { // Check for collision with a tank: if (otherState.GetTankExtent().ContainsPoint(bulletState.Pos)) { #if DEBUG collisionStatuses[i] |= CollisionStatus.WithBullet; collisionStatuses[b] |= CollisionStatus.WithTank; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.MobileStates[i] = otherState.Kill(); } } else { // Check for collision with another bullet: if (otherState.Pos == bulletState.Pos) { #if DEBUG collisionStatuses[b] |= CollisionStatus.WithBullet; collisionStatuses[i] |= CollisionStatus.WithBullet; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.MobileStates[i] = otherState.Kill(); } } } // Check for collisions with bases: for (int p = 0; p < Constants.PLAYERS_PER_GAME; p++) { Base @base = Game.Current.Players[p].Base; if (bulletState.Pos == @base.Pos) { #if DEBUG collisionStatuses[b] |= CollisionStatus.WithBase; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.Outcome |= (Outcome)(((byte)Outcome.Player1BaseKilled) << p); } } } } }
protected override void CollisionAfter(Collidable <MonkeyBoard> otherCollidable, CollisionStatus direction) { if (otherCollidable is Monkey || otherCollidable is Crate) { if (direction == CollisionStatus.CollisionFromTop) { Board.RemovePositional(this); if (AIKilledSound != null) { AIKilledSound.Play(); } } } base.CollisionAfter(otherCollidable, direction); }
/// <summary> /// Separate out the moving of bullets from moving of tanks. /// This is so that distance calculations can take place AFTER the bullets are moved. /// ISSUE: What about when the board shrinks after time is up? /// That could cause bullets to be removed during the next game tick, before they move. /// </summary> /// <param name="gameState"></param> public static void MoveBulletsTwice(MutableGameState gameState, IList <Point> wallsRemoved) { for (byte phase = 0; phase < 2; phase++) { #if DEBUG CollisionStatus[] collisionStatuses = new CollisionStatus[Constants.ALL_ELEMENT_COUNT]; #endif Point[] wallsShot = new Point[MAX_WALLS_SHOT]; byte wallsShotCount = 0; for (int b = Constants.MIN_BULLET_INDEX; b <= Constants.MAX_BULLET_INDEX; b++) { MobileState bulletState = gameState.MobileStates[b]; if (!bulletState.IsActive) { continue; } bulletState = bulletState.Move(); gameState.MobileStates[b] = bulletState; if (bulletState.Pos.X < 0 || bulletState.Pos.Y < 0 || bulletState.Pos.X >= Game.Current.BoardWidth || bulletState.Pos.Y >= Game.Current.BoardHeight) { #if DEBUG collisionStatuses[b] |= CollisionStatus.WithOutOfBoundsArea; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; } else { if (gameState.Walls[bulletState.Pos]) { bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; /* Shoot out the walls on the segment. * Assume that they are all in bounds since the tank could not have shot the bullet otherwise * TODO: What to do when the board shrinks after the time limit is up, * as then part of the segment could be out of bounds? */ int first; int last; int x; int y; switch (bulletState.Dir) { case Direction.LEFT: case Direction.RIGHT: x = bulletState.Pos.X; first = bulletState.Pos.Y - 2; last = bulletState.Pos.Y + 2; for (y = first; y <= last; y++) { if (gameState.Walls[x, y]) { wallsShot[wallsShotCount] = new Point((short)x, (short)y); wallsShotCount++; } } break; case Direction.DOWN: case Direction.UP: y = bulletState.Pos.Y; first = bulletState.Pos.X - 2; last = bulletState.Pos.X + 2; for (x = first; x <= last; x++) { if (gameState.Walls[x, y]) { wallsShot[wallsShotCount] = new Point((short)x, (short)y); wallsShotCount++; } } break; } } else { // Check for swapping places with another bullet: Direction oppositeDir = bulletState.Dir.GetOpposite(); // Check if colliding with a bullet that hasn't moved yet, and which is moving in the opposite direction: for (int otherBulletIndex = b + 1; otherBulletIndex <= Constants.MAX_BULLET_INDEX; otherBulletIndex++) { MobileState otherBulletState = gameState.MobileStates[otherBulletIndex]; if (otherBulletState.IsActive && (otherBulletState.Dir == oppositeDir) && (otherBulletState.Pos == bulletState.Pos)) { // Collision with another bullet #if DEBUG collisionStatuses[b] |= CollisionStatus.WithBullet; collisionStatuses[otherBulletIndex] |= CollisionStatus.WithBullet; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.MobileStates[otherBulletIndex] = otherBulletState.Kill(); break; } // TODO: What happens if two bullets collide, but then a tank moves into the two bullets? // Check with Entelect and ensure logic works for this case } } } } // Check for collisions between bullets, tanks and bases: for (int b = Constants.MIN_BULLET_INDEX; b <= Constants.MAX_BULLET_INDEX; b++) { MobileState bulletState = gameState.MobileStates[b]; // Only if bullet is active: if (!bulletState.IsActive) { continue; } for (int i = 0; i < Constants.MOBILE_ELEMENT_COUNT; i++) { // Only if not self: if (i == b) { continue; } // Only check active elements: MobileState otherState = gameState.MobileStates[i]; if (!otherState.IsActive) { continue; } if (i < Constants.TANK_COUNT) { // Check for collision with a tank: if (otherState.GetTankExtent().ContainsPoint(bulletState.Pos)) { #if DEBUG collisionStatuses[i] |= CollisionStatus.WithBullet; collisionStatuses[b] |= CollisionStatus.WithTank; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.MobileStates[i] = otherState.Kill(); } } else { // Check for collision with another bullet: if (otherState.Pos == bulletState.Pos) { #if DEBUG collisionStatuses[b] |= CollisionStatus.WithBullet; collisionStatuses[i] |= CollisionStatus.WithBullet; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.MobileStates[i] = otherState.Kill(); } } } // Check for collisions with bases: for (int p = 0; p < Constants.PLAYERS_PER_GAME; p++) { Base @base = Game.Current.Players[p].Base; if (bulletState.Pos == @base.Pos) { #if DEBUG collisionStatuses[b] |= CollisionStatus.WithBase; #endif bulletState = bulletState.Kill(); gameState.MobileStates[b] = bulletState; gameState.Outcome |= (Outcome)(((byte)Outcome.Player1BaseKilled) << p); } } } // Remove walls that were shot: for (int w = 0; w < wallsShotCount; w++) { if (wallsRemoved == null) { gameState.Walls[wallsShot[w]] = false; } else { Point wall = wallsShot[w]; if (gameState.Walls[wall]) { gameState.Walls[wall] = false; wallsRemoved.Add(wall); } } } } }
protected override void DoCheck(IMovable user, ref Vector2 vel, ref CollisionStatus collStatus, LayerMask mask, Bounds2D bounds, Bounds2D shrinkedBounds) { var direction = (int)Mathf.Sign(vel.x); var bMin = bounds.Min; var bMax = bounds.Max; var horizontalRays = user.HorizontalRaycasts; var directionVector = new Vector2(vel.x * Time.deltaTime, 0); var positiveDir = direction == 1; var originX = positiveDir ? bMax.x : bMin.x; var originY = shrinkedBounds.Min.y; var origin = new Vector2(originX, originY); var height = shrinkedBounds.Size.y; var spacing = height / (horizontalRays - 1); var rayLength = directionVector.magnitude; LastHit = null; var config = user.GetMotorConfig <GroundMotorConfig>(); var maxAngle = config == null ? 0 : config.MaxAngle; for (var y = 0; y < horizontalRays; y++) { var raycast = Physics2D.Raycast(origin, directionVector, rayLength, mask); Debug.DrawRay(origin, directionVector, raycast ? Color.green : Color.red); if (raycast && !raycast.collider.isTrigger && raycast.distance < rayLength) { LastHit = raycast; collStatus.Left = direction == -1; collStatus.Right = direction == 1; var slopeAngle = Vector2.Angle(raycast.normal, Vector2.up); if (config != null) { if (slopeAngle > maxAngle) { //Hit wall vel.x = raycast.distance / Time.deltaTime * direction; rayLength = raycast.distance; /* if (Collisions.ClimbingSlope) { * moveAmount.y = Mathf.Tan(Collisions.SlopeAngle * Mathf.Deg2Rad) * Mathf.Abs(moveAmount.x); * }*/ continue; } } else { //Hit wall if (Mathf.RoundToInt(slopeAngle) % 90 == 0) { vel.x = 0; } else { vel.x = raycast.distance / Time.deltaTime * direction; rayLength = raycast.distance; vel.y = Mathf.Tan(slopeAngle * Mathf.Deg2Rad) * Mathf.Abs(vel.x); continue; } } } origin.y += spacing; } if (LastHit != null) { return; } collStatus.Left = false; collStatus.Right = false; }