Skip to content

Instantly share code, notes, and snippets.

@eman41
Last active December 16, 2015 16:08
Show Gist options
  • Save eman41/5460580 to your computer and use it in GitHub Desktop.
Save eman41/5460580 to your computer and use it in GitHub Desktop.
A movement controller for platforms from an XNA prototype I've been working on. Platforms using this will move back and forth between two points, stopping at each end for the hold-time specified. This is part of a composition-based API, so you can drop in a controller, a sensor, etc and create platforms of various flavors. An option exists to fo…
using System;
using Microsoft.Xna.Framework;
public class PlatformController : IResetable
{
private Vector2 _startPos = Vector2.Zero;
private Vector2 _stopPos = Vector2.Zero;
private float _speed = 0f;
private bool _beingHeld = false;
private bool _moveOneTime = false;
private bool _holdingUntilReset = false;
private Timer _holdTimer;
private Vector2 _heldVelocity = Vector2.Zero;
/// <summary>
/// Initializes a new platform controller.
/// Start/Strop should be given in level tile coordinates.
/// </summary>
public PlatformController(Vector2 start, Vector2 stop, float speed,
TimeSpan holdTime, bool oneTime = false)
{
_holdTimer = new Timer(holdTime);
_moveOneTime = oneTime;
_startPos = TranslateToPixelCoords(start);
_stopPos = TranslateToPixelCoords(stop);
_speed = speed;
Reset();
}
private Vector2 TranslateToPixelCoords(Vector2 levelCoords)
{
float x = levelCoords.X * GameMap.TILE_SIZE;
float y = levelCoords.Y * GameMap.TILE_SIZE;
return new Vector2(x, y);
}
private Vector2 GetStartVelocity(Vector2 start, Vector2 stop, float speed)
{
Vector2 moveNormal = new Vector2(start.X - stop.X, start.Y - stop.Y);
moveNormal.Normalize();
return new Vector2(moveNormal.X * speed, moveNormal.Y * speed);
}
/// <summary>Current X, Y pixel coordinates of this platform</summary>
public Vector2 Position { get; set; }
/// <summary>Current platform speed</summary>
public Vector2 Velocity { get; set; }
/// <summary>Reset this entity to it's starting state</summary>
public void Reset()
{
Position = _startPos;
Velocity = GetStartVelocity(_startPos, _stopPos, _speed);
_beingHeld = false;
_holdingUntilReset = false;
_holdTimer.Reset();
}
/// <summary>Update the state of the platform.</summary>
/// <param name="gameTime">Current Game time</param>
public void Update(GameTime gameTime)
{
if (_beingHeld)
{
if (HoldDurationExceeded(gameTime.ElapsedGameTime))
{
ReleaseAndReverseDirection();
}
else
{
ClampToNearestEndpoint();
}
}
else if (!BetweenEndPoints())
{
HoldPlatform();
}
UpdatePosition();
}
private bool HoldDurationExceeded(TimeSpan elapsed)
{
// AdvanceTimerCyclic returns true when it resets.
return _holdTimer.AdvanceTimerCyclic(elapsed);
}
private void ReleaseAndReverseDirection()
{
_beingHeld = false;
Velocity = -1 * _heldVelocity;
}
private void ClampToNearestEndpoint()
{
Velocity = Vector2.Zero;
Position = IsCloserToStart() ? _startPos : _stopPos;
_holdingUntilReset = _moveOneTime && Position.Equals(_stopPos);
}
private bool IsCloserToStart()
{
return DistanceTo(_startPos) < DistanceTo(_stopPos);
}
private float DistanceTo(Vector2 vec)
{
return Vector2.Distance(vec, Position);
}
private bool BetweenEndPoints()
{
// Look ahead to avoid bouncing.
return MathUtilities.PointBetween(_startPos, _stopPos, Position + Velocity);
}
private void HoldPlatform()
{
_beingHeld = true;
_heldVelocity = Velocity;
Velocity = Vector2.Zero;
}
private void UpdatePosition()
{
if (!_holdingUntilReset)
{
Position += Velocity;
}
}
}
@eman41
Copy link
Author

eman41 commented Apr 25, 2013

Namespace/Package utilities not included:

  • IResetable: Command interface providing the Reset() function.
  • Timer: Wrapper class for TimeSpan, provides an advance method that restarts when it reaches 0.
  • MathUtilities: Vector math not provided by XNA.
  • GameMap: Class for game maps/levels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment