Skip to content

Instantly share code, notes, and snippets.

@tomdalling
Created March 20, 2013 02:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomdalling/5201751 to your computer and use it in GitHub Desktop.
Save tomdalling/5201751 to your computer and use it in GitHub Desktop.
Some code from a state machine for Oddworld-style platform movement.
//
// Copyright 2012. All rights reserved.
//
static const int StandingHeight = 2;
static const int CrouchingHeight = 1;
class State {
public:
virtual void begin(PlayerController* p) {}
virtual void end(PlayerController* p) {}
virtual State* update(PlayerController* p, float secondsElapsed) {
return NULL;
}
protected:
bool isAirAt(PlayerController* p, int mapX, int mapY) const {
if(mapX < 0 || mapY < 0 || mapX >= (int)p->_map->width() || mapY >= (int)p->_map->height())
return false;
return (p->_map->tileAt(mapX, mapY).type == TiledPlatformMap::Tile::Type_Air);
}
bool canMoveBy(PlayerController* p, int deltaX, unsigned height) const {
assert(height > 0);
Vec2<int> pos = p->_pos;
for(unsigned h = 0; h < height; ++h){
if(!isAirAt(p, pos.x + deltaX, pos.y + h)){
return false;
}
}
return true;
}
bool isAboveEdge(PlayerController* p) const {
if(p->_pos.y <= 0)
return false;
return (p->_map->tileAt(p->_pos.x, p->_pos.y - 1).type == TiledPlatformMap::Tile::Type_PlatformEdge);
}
bool isBelowEdge(PlayerController* p) const {
if(p->_pos.y >= (int)p->_map->height() - StandingHeight)
return false;
return (p->_map->tileAt(p->_pos.x, p->_pos.y + StandingHeight).type == TiledPlatformMap::Tile::Type_PlatformEdge);
}
bool doesEdgeRequireLeftFacing(PlayerController* p, int edgeDeltaY) const {
Vec2<int> edge = p->_pos + Vec2<int>(0, edgeDeltaY);
if(edge.x == 0) {
return false;
} else if(edge.x == p->_map->width() - 1) {
return true;
} else {
if(isAirAt(p, edge.x - 1, edge.y)){
return false;
} else if(isAirAt(p, edge.x + 1, edge.y)) {
return true;
} else {
return true; //can face either direction
}
}
}
void fallPlayer(PlayerController* p) {
while(isAirAt(p, p->_pos.x, p->_pos.y - 1)){
p->_pos.y -= 1;
}
}
bool controlIsPressed(PlayerController* p, Control c) {
return ((p->_pressedControls & c) == c);
}
};
class StandingIdle : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-sil.anim.json");
} else {
p->_setAnim("anim:///anims/player-sir.anim.json");
}
}
State* update(PlayerController* p, float secondsElapsed) {
if(controlIsPressed(p, Control_Down))
return &p->_s_StandToCrouch;
if(controlIsPressed(p, Control_Up) && isBelowEdge(p)){
if(!doesEdgeRequireLeftFacing(p, StandingHeight) == !p->_facingLeft){
return &p->_s_LedgeJumpUp;
} else {
return &p->_s_StandingTurn;
}
}
if((!p->_facingLeft && controlIsPressed(p, Control_Right) && canMoveBy(p, 1, StandingHeight)) ||
(p->_facingLeft && controlIsPressed(p, Control_Left) && canMoveBy(p, -1, StandingHeight)))
{
return &p->_s_StandingMove;
}
if((controlIsPressed(p, Control_Left) && !p->_facingLeft) ||
(controlIsPressed(p, Control_Right) && p->_facingLeft))
{
return &p->_s_StandingTurn;
}
return NULL;
}
} _s_StandingIdle;
class CrouchingIdle : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-cil.anim.json");
} else {
p->_setAnim("anim:///anims/player-cir.anim.json");
}
}
State* update(PlayerController* p, float secondsElapsed) {
if(controlIsPressed(p, Control_Up) && canMoveBy(p, 0, StandingHeight))
return &p->_s_CrouchToStand;
if(controlIsPressed(p, Control_Down) && isAboveEdge(p)){
if(!doesEdgeRequireLeftFacing(p, -1) == !p->_facingLeft){
return &p->_s_LedgeClimbDown;
} else {
return &p->_s_CrouchingTurn;
}
}
if((controlIsPressed(p, Control_Left) && !p->_facingLeft) ||
(controlIsPressed(p, Control_Right) && p->_facingLeft))
{
return &p->_s_CrouchingTurn;
}
if((controlIsPressed(p, Control_Right) && canMoveBy(p, 1, CrouchingHeight)) ||
(controlIsPressed(p, Control_Left) && canMoveBy(p, -1, CrouchingHeight)))
{
return &p->_s_CrouchingMove;
}
return NULL;
}
} _s_CrouchingIdle;
class StandingMove : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-sml.anim.json");
p->_pos.x -= 1;
} else {
p->_setAnim("anim:///anims/player-smr.anim.json");
}
}
void end(PlayerController* p) {
if(!p->_facingLeft){
p->_pos.x += 1;
}
fallPlayer(p);
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_StandingIdle;
} else {
return NULL;
}
}
} _s_StandingMove;
class CrouchingMove : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-cml.anim.json");
p->_pos.x -= 1;
} else {
p->_setAnim("anim:///anims/player-cmr.anim.json");
}
}
void end(PlayerController* p) {
if(!p->_facingLeft){
p->_pos.x += 1;
}
fallPlayer(p);
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_CrouchingIdle;
} else {
return NULL;
}
}
} _s_CrouchingMove;
class CrouchingTurn : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-ctr.anim.json");
} else {
p->_setAnim("anim:///anims/player-ctl.anim.json");
}
}
void end(PlayerController* p) {
p->_facingLeft = !p->_facingLeft;
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_CrouchingIdle;
} else {
return NULL;
}
}
} _s_CrouchingTurn;
class StandingTurn : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-str.anim.json");
} else {
p->_setAnim("anim:///anims/player-stl.anim.json");
}
}
void end(PlayerController* p) {
p->_facingLeft = !p->_facingLeft;
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_StandingIdle;
} else {
return NULL;
}
}
} _s_StandingTurn;
class LedgeJumpUp : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-ljl.anim.json");
} else {
p->_setAnim("anim:///anims/player-ljr.anim.json");
}
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_LedgeIdle;
} else {
return NULL;
}
}
} _s_LedgeJumpUp;
class LedgeIdle : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-lil.anim.json");
} else {
p->_setAnim("anim:///anims/player-lir.anim.json");
}
}
State* update(PlayerController* p, float secondsElapsed) {
if(controlIsPressed(p, Control_Up)){
return &p->_s_LedgeClimbUp;
}
if(controlIsPressed(p, Control_Down)){
return &p->_s_LedgeJumpDown;
}
return NULL;
}
} _s_LedgeIdle;
class LedgeClimbUp : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-lcl.anim.json");
} else {
p->_setAnim("anim:///anims/player-lcr.anim.json");
}
}
void end(PlayerController* p) {
p->_pos.y += 3;
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_CrouchingIdle;
} else {
return NULL;
}
}
} _s_LedgeClimbUp;
class LedgeClimbDown : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-lll.anim.json");
} else {
p->_setAnim("anim:///anims/player-llr.anim.json");
}
p->_pos.y -= 3;
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_LedgeIdle;
} else {
return NULL;
}
}
} _s_LedgeClimbDown;
class LedgeJumpDown : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-ldl.anim.json");
} else {
p->_setAnim("anim:///anims/player-ldr.anim.json");
}
}
void end(PlayerController* p) {
fallPlayer(p);
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_StandingIdle;
} else {
return NULL;
}
}
} _s_LedgeJumpDown;
class StandToCrouch : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-s2cl.anim.json");
} else {
p->_setAnim("anim:///anims/player-s2cr.anim.json");
}
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_CrouchingIdle;
} else {
return NULL;
}
}
} _s_StandToCrouch;
class CrouchToStand : public State {
void begin(PlayerController* p) {
if(p->_facingLeft){
p->_setAnim("anim:///anims/player-c2sl.anim.json");
} else {
p->_setAnim("anim:///anims/player-c2sr.anim.json");
}
}
State* update(PlayerController* p, float secondsElapsed) {
if(p->_anim.isFinished()){
return &p->_s_StandingIdle;
} else {
return NULL;
}
}
} _s_CrouchToStand;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment