/// <summary> /// Assigns the colliding objects and their touching directions. /// </summary> /// <param name="objectA">The first object involved in the collision.</param> /// <param name="objectB">The second object involved in the collision.</param> /// <param name="touchingA">The direction that the first object is colliding in.</param> /// <param name="touchingB">The direction that the second object is colliding in.</param> public GenCollideEvent(GenObject objectA, GenObject objectB, GenObject.Direction touchingA, GenObject.Direction touchingB) { ObjectA = objectA; ObjectB = objectB; TouchingA = touchingA; TouchingB = touchingB; // TODO: Return the force of the collision as well. }
/// <summary> /// Flags the given edge of a specific tile as open. /// </summary> /// <param name="x">The column position of the tile, starting from 0.</param> /// <param name="y">The row position of the tile, starting from 0.</param> /// <param name="edge">The direction of the edge to flag as closed.</param> /// <returns>True if the tile edge was flagged as open, false if not.</returns> public bool OpenTileEdge(int x, int y, GenObject.Direction edge) { if (Tiles[y][x] == null) return false; Tiles[y][x].OpenEdges |= edge; if (TileSheetTexture != null) GetAutoTile(x, y); return true; }
/// <summary> /// Applies collision detection and response between an object and the tiles of the tilemap. /// Uses an object's position to efficiently find neighboring tiles to check for collision in the tiles two-dimensional array. /// </summary> /// <param name="gameObject">The object to check for collisions.</param> /// <param name="callback">The delegate method that will be invoked if a collision occurs.</param> /// <returns>True is a collision occurs, false if not.</returns> public bool CollideObject(GenObject gameObject, CollideEvent callback) { GetTileBounds( gameObject.MoveBounds.Left, gameObject.MoveBounds.Right, gameObject.MoveBounds.Top, gameObject.MoveBounds.Bottom); bool collided = false; for (int y = _tileBounds[2]; y <= _tileBounds[3]; y++) { for (int x = _tileBounds[0]; x <= _tileBounds[1]; x++) { // If the tile is empty, do not check for a collision. if (Tiles[y][x] != null) { if (GenCollide.Collide(gameObject, Tiles[y][x], callback, Tiles[y][x].OpenEdges)) collided = true; } } } return collided; }
/// <summary> /// Sets up a control scheme for moving a given object. /// </summary> /// <param name="controlObject">The object that is controlled.</param> /// <param name="controlMode">The type of control available to the control object.</param> /// <param name="movementType">How acceleration or velocity should be handled as the object moves.</param> /// <param name="stoppingType">How acceleration or velocity should be handled as the object stops.</param> /// <param name="playerIndex">The index number of the player controlling the object.</param> public GenControl(GenObject controlObject, ControlType controlMode = ControlType.TopDown, Movement movementType = Movement.Instant, Stopping stoppingType = Stopping.Instant, PlayerIndex playerIndex = PlayerIndex.One) { ControlMode = controlMode; ControlObject = controlObject; MovementType = movementType; StoppingType = stoppingType; PlayerIndex = playerIndex; _keyboardControls = new Keys[5]; _gamePadControls = new Buttons[5]; ButtonsSpecial = GenGamePad.ButtonsSpecial.None; UseInput = true; #if WINDOWS UseKeyboard = true; #endif UseGamePad = true; MovementSpeedX = 0; MovementSpeedY = 0; JumpSpeed = 0; JumpInheritVelocity = false; JumpCount = 1; // Start the jump counter at 1, since the control object spawns in the air initially. _jumpCounter = 1; Gravity = Vector2.Zero; _inAir = true;_state = State.Idle; MoveState = GenObject.Direction.None; IdleAnimation = null; MoveAnimation = null; JumpAnimation = null; FallAnimation = null; UseSpeedAnimation = false; MinAnimationFps = 0f; MaxAnimationFps = 12f; JumpCallback = null; LandCallback = null; // Set the default movement direction keyboard controls. SetDirectionControls(Keys.Left, Keys.Right, Keys.Up, Keys.Down); // Set the default jumping keyboard control. SetJumpControl(Keys.Space); // Set the default movement direction game pad controls. SetDirectionControls(Buttons.LeftThumbstickLeft, Buttons.LeftThumbstickRight, Buttons.LeftThumbstickUp, Buttons.LeftThumbstickDown); // Set the default jumping game pad control. SetJumpControl(Buttons.A); }
/// <summary> /// Sets a given object as the parent of this object, using the specified parenting type. /// </summary> /// <param name="gameObject">The object to set as the parent object.</param> /// <param name="parentMode">The type of transformations to connect with the parent object.</param> public void SetParent(GenObject gameObject, ParentType parentMode) { Parent = gameObject; ParentMode = parentMode; }
/// <summary> /// Override this method to add additional pre-update logic. /// </summary> public override void PreUpdate() { OldVelocity = Velocity; // Reset the bit fields for collision flags. OldTouching = Touching; Touching = Direction.None; OldPlatform = Platform; Platform = null; }
/// <summary> /// A physical object that can be moved around in screen space. /// </summary> /// <param name="x">The x position of the top-left corner of the object.</param> /// <param name="y">The y position of the top-left corner of the object.</param> /// <param name="width">The width of the object.</param> /// <param name="height">The height of the object.</param> public GenObject(float x, float y, float width, float height) { _position = new Vector2(x, y); _oldPosition = _position; _hasMoved = false; _bounds = new GenAABB(x, y, width, height); _centerPosition = new Vector2(x + _bounds.HalfWidth, y + _bounds.HalfHeight); _origin = new Vector2(_bounds.HalfWidth, _bounds.HalfHeight); _originPosition = new Vector2(x + _origin.X, y + _origin.Y); _boundingRect = new Rectangle(0, 0, (int)width, (int)height); _rotation = 0f; RotationSpeed = 0f; _moveDistance = Vector2.Zero; _moveBounds = new GenAABB(x, y, width, height); Immovable = false; Solid = true; Mass = 1f; Velocity = Vector2.Zero; OldVelocity = Velocity; Acceleration = Vector2.Zero; Deceleration = Vector2.Zero; MaxVelocity = Vector2.Zero; _facing = Direction.None; OldTouching = Direction.None; Touching = Direction.None; Parent = null; ParentMode = ParentType.None; ParentOffset = Vector2.Zero; IsPlatform = false; Platform = null; OldPlatform = null; Path = null; PathNodeIndex = 0; PathDirection = 1; PathSpeed = 0f; PathType = GenPath.Type.Clockwise; PathAxis = GenMove.Axis.Both; PathMovement = GenPath.Movement.Instant; }
/// <summary> /// Checks for overlaps or collisions between the given game object and objects within the node and its leaf nodes. /// A broad phase check is done by only checking for overlaps or collisions against objects within the leaf nodes that intersect with the given game object. /// </summary> /// <param name="gameObject">The game object to check for overlaps or collisions.</param> /// <param name="callback">The delegate method that will be invoked if an overlap occurs.</param> /// <param name="separate">A flag used to determine if objects should collide with each other.</param> /// <param name="collidableEdges">A bit field of flags determining which edges of the quadtree objects are collidable.</param> /// <returns>True if an overlap occurs, false if not.</returns> internal bool Overlap(GenObject gameObject, CollideEvent callback, bool separate, GenObject.Direction collidableEdges) { bool overlap = false; if (_nodes[0] != null) { // If the game object's movement bounds intersects with any leaf node, do overlap and collision checks against any objects in that node. if (gameObject.MoveBounds.Intersects(this)) { if (gameObject.MoveBounds.Top <= _midpointY) { if (gameObject.MoveBounds.Left <= _midpointX) // Top-left leaf node. { if (_nodes[0].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } if (gameObject.MoveBounds.Right >= _midpointX) // Top-right leaf node. { if (_nodes[1].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } } if (gameObject.MoveBounds.Bottom >= _midpointY) { if (gameObject.MoveBounds.Left <= _midpointX) // Bottom-left leaf node. { if (_nodes[2].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } if (gameObject.MoveBounds.Right >= _midpointX) // Bottom-right leaf node. { if (_nodes[3].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } } } } // Check for overlaps and collisions against this node's objects. if (OverlapObjects(gameObject, callback, separate, collidableEdges)) overlap = true; return overlap; }
/// <summary> /// Inserts a given object by adding it to this quadtree node's objects list, and sets this node as its current location. /// </summary> /// <param name="gameObject">The <c>GenObject</c> to insert into this node.</param> protected void Insert(GenObject gameObject) { // If the game object is already contained within the node, do not insert the same object twice. _objects.Add(gameObject); // Set the object's location to this node. if (_quadtree._objectLocations.ContainsKey(gameObject)) _quadtree._objectLocations[gameObject] = this; else _quadtree._objectLocations.Add(gameObject, this); }
/// <summary> /// Checks for overlaps or collisions against each object within this node. /// </summary> /// <param name="gameObject">The game object to check for overlaps or collisions.</param> /// <param name="callback">The delegate method that will be invoked if an overlap occurs.</param> /// <param name="separate">A flag used to determine if objects should collide with each other.</param> /// <param name="collidableEdges">A bit field of flags determining which edges of the quadtree objects are collidable.</param> /// <returns>True if an overlap occurs, false if not.</returns> public bool OverlapObjects(GenObject gameObject, CollideEvent callback, bool separate, GenObject.Direction collidableEdges) { bool overlap = false; foreach (GenObject nodeObject in _objects) { if (separate) { if (GenCollide.Collide(gameObject, nodeObject, callback, collidableEdges)) overlap = true; } else { if (GenCollide.Overlap(gameObject, nodeObject, callback)) overlap = true; } } return overlap; }
internal void Add(GenObject gameObject) { if (_nodes[0] == null) { Insert(gameObject); // If the node is full, attempt to add each of its objects into a leaf node. if ((_objects.Count >= _quadtree.MaxObjects) && (_level < _quadtree.MaxLevels)) { int nextLevel = _level + 1; // Create the leaf nodes. _nodes[0] = new GenQuadtreeNode(_minX, _minY, _halfWidth, _halfHeight, _quadtree, this, nextLevel); _nodes[1] = new GenQuadtreeNode(_midpointX, _minY, _halfWidth, _halfHeight, _quadtree, this, nextLevel); _nodes[2] = new GenQuadtreeNode(_minX, _midpointY, _halfWidth, _halfHeight, _quadtree, this, nextLevel); _nodes[3] = new GenQuadtreeNode(_midpointX, _midpointY, _halfWidth, _halfHeight, _quadtree, this, nextLevel); // Iterate through the objects list in reverse, attempting to add each object into a leaf node. // Otherwise, leave the object in this node. for (int i = _objects.Count - 1; i >= 0; i--) { int index = GetIndex(_objects[i].MoveBounds); if (index != -1) { _nodes[index].Add(_objects[i]); _objects.Remove(_objects[i]); } } } } else { // Since leaf nodes have already been created, attempt to add the object into a leaf node. // Otherwise, add the object to this node. int index = GetIndex(gameObject.MoveBounds); if (index != -1) _nodes[index].Add(gameObject); else Insert(gameObject); } _objectCount++; }
public bool OverlapSelf(CollideEvent callback, bool separate, GenObject.Direction collidableEdges) { bool overlap = false; foreach (GenObject gameObject in _activeObjects) { GenQuadtreeNode parentNode = _objectLocations[gameObject]._parentNode; // Iterate up the parent nodes, and do overlap and collision checks against their objects. while (parentNode != null) { if (parentNode.OverlapObjects(gameObject, callback, separate, collidableEdges)) overlap = true; parentNode = parentNode._parentNode; } // Do overlap and collision checks against objects within its containing node and any remaining leaf nodes. if (_objectLocations[gameObject].Overlap(gameObject, callback, separate, collidableEdges)) overlap = true; } return overlap; }
/// <summary> /// Checks for overlaps or collisions between the given game object and objects within the quadtree. /// A broad phase check is done by only checking for overlaps or collisions against objects within the nodes that intersect with the given game object. /// </summary> /// <param name="gameObject">The game object to check for overlaps or collisions.</param> /// <param name="callback">The delegate method that will be invoked if an overlap occurs.</param> /// <param name="separate">A flag used to determine if objects should collide with each other.</param> /// <param name="collidableEdges">A bit field of flags determining which edges of the quadtree objects are collidable.</param> /// <returns>True if an overlap occurs, false if not.</returns> public bool Overlap(GenObject gameObject, CollideEvent callback, bool separate, GenObject.Direction collidableEdges) { return _rootNode.Overlap(gameObject, callback, separate, collidableEdges); }
/// <summary> /// Rotates an object around a given point. /// </summary> /// <param name="gameObject">The object to rotate around the point.</param> /// <param name="point">The point to rotate around.</param> /// <param name="angle">The angle of rotation, in degrees.</param> /// <param name="radius">The distance from the point that the object will be placed.</param> public static void RotateAroundPoint(GenObject gameObject, Vector2 point, float angle, float radius) { Vector2 rotationPosition = Vector2.Zero; rotationPosition = AngleToVector(angle); rotationPosition = point + rotationPosition * radius; gameObject.X = rotationPosition.X; gameObject.Y = rotationPosition.Y; }
/// <summary> /// Gets a normalized vector of the x and y distances from an object to a point. /// Useful for calculating the distribution between horizontal and vertical speeds needed to meet an overall speed. /// </summary> /// <param name="gameObject">The object to start the distance calculation from.</param> /// <param name="point">The point to end the distance calculation at.</param> /// <param name="axis">A bit field of flags to determine which axis should be included.</param> /// <returns>The normalized distance vector.</returns> public static Vector2 GetDistanceNormal(GenObject gameObject, Vector2 point, Axis axis = Axis.Both) { Vector2 distance = Vector2.Zero; // Get the x and y distances between the object and the point. distance.X = ((axis & Axis.Horizontal) == Axis.Horizontal) ? (point.X - gameObject.Position.X) : 0f; distance.Y = ((axis & Axis.Vertical) == Axis.Vertical) ? (point.Y - gameObject.Position.Y) : 0f; // Return the normalized distance vector. return GenU.NormalizeVector2(distance); }
/// <summary> /// Determines if an object will reach a specified point on the next update. /// Uses the current position and movement direction of the object, and a given speed. /// The current velocity of the object will be used if the given speed is 0. /// </summary> /// <param name="gameObject">The moving object to check.</param> /// <param name="point">The point needed to reach.</param> /// <param name="speed">The velocity to move the object at. A value of 0 will use the current velocity of the object.</param> /// <param name="radius">The radius extending from the point position to use as a search area.</param> /// <param name="axis">The movement axis to include in the check.</param> /// <returns>True if the object will reach the point, false if not.</returns> public static bool CanReachPoint(GenObject gameObject, Vector2 point, float speed = 0, float radius = 0, Axis axis = Axis.Both) { // Calculate the minimum distance that the object will need to move to reach the point. float minDistanceX = ((axis & Axis.Horizontal) == Axis.Horizontal) ? (gameObject.Position.X - point.X) : 0; float minDistanceY = ((axis & Axis.Vertical) == Axis.Vertical) ? (gameObject.Position.Y - point.Y) : 0; float minDistance = (float)Math.Sqrt(minDistanceX * minDistanceX + minDistanceY * minDistanceY); Vector2 newPosition = Vector2.Zero; // Calculate the next position of the object using its current direction. // Use the current velocity of the object if speed is 0. if (speed == 0) newPosition = Vector2.Add(gameObject.Position, gameObject.Velocity * GenG.TimeStep); else newPosition = Vector2.Add(gameObject.Position, GenU.NormalizeVector2(gameObject.Velocity) * speed * GenG.TimeStep); // Calculate the distance that the object will move. float moveDistance = Vector2.Distance(gameObject.Position, newPosition); return (moveDistance + radius) >= minDistance ; }