This game is a simplified version of TMNT 4: Turtles in Time, a classic "beat 'em up" 2D scroller game on SNES. I utilized Redux to track character states, React and CSS to animate sprites and program the motion of characters.
Click here. Press 'RIGHT' to move forward, 'UP' to jump, and 'SPACE' to attack.
I used three components to setup the display frame:
Gamecreates adivto define the frame width and height.Viewportis positionedabsoluterelative toGameStageis a child ofViewport, renders the location/environment and is sized with the full width of the stage level (i.e., the full distance that the turtle may potentially travel). This is where theTurtlecomponent lives.
Moving the Turtle is simply updating its absolute left position relative to Stage, which is tracked via Redux. Viewport listens for changes to the turtle's left value, and sets its own left position as the negative of the turtle's position, when the Turtle reaches at least 1/4 through the Game frame width. Because Viewport is positioned absolute, a negative left position would place it to the left of the Game frame, creating the illusion of panning from left to right. When the Viewport changes its position as the turtle changes its position, it "follows" the turtle.
componentWillReceiveProps(newProps) {
const {turtle: {pos: {left}}} = newProps;
let newLeft;
if (left > (FRAME_WIDTH / 4)) {
newLeft = left * (-1) + (FRAME_WIDTH / 4);
}
this.setState({left: newLeft});
}To accomplish a combo attack, I use an array that tracks the number of times a player presses the 'attack' key. The Turtle component renders the appropriate sprites based on a set of conditions to determine if it's a combo attack.
setComboAttackSprite() {
let attackSprite;
if (this.combo.length === 1) {
attackSprite = 'attack-1';
} else if (this.combo.length === 2 && this.combo[1] - this.combo[0] < 450) {
// if it's the second keydown & duration betw key presses is less than half sec, render the second attack sprite
attackSprite = 'attack-2';
} else if (this.combo.length === 3 && this.combo[2] - this.combo[1] < 450) {
// if it's the third keydown & duration betw key presses is less than half sec, render the third attack sprite
attackSprite = 'attack-3';
this.combo = [];
} else {
// resets if the subsequent keydown is not within half sec of the prev keydown
attackSprite = 'attack-1';
this.combo = [];
}
return attackSprite;
}While the event handlers for controls are defined in the Turtle component, the logic for the interaction between the Turtle and Foot soldiers lives in the Foot component. Foot is a Redux container that listens for changes to Turtle position and actions (i.e., move or attack). This would minimize the number of operations in the game as the Turtle does not have to keep track of all the Foot soldiers in front of it, and only the Foot nearest to the turtle would update and re-render.
- when
Turtleapproaches aFoot,Footis "activated" by moving forward and attacking. - only the nearest
Footresponds and re-renders when theTurtleapproaches - additionally, while every
Footlistens forTurtlechanges, any change toTurtlestate does not cause a re-render toFoot
shouldComponentUpdate(nextProps, nextState) {
//re-render only if foot React state VALUES changed (i.e., ignore turtle state changes)
if (JSON.stringify(nextState) !== JSON.stringify(this.state)) {
return true;
}
return false;
}Footlistens forTurtleattacks, and reduces health when the attack lands within theFoot's hitbox
if (turtle.doing.includes('attack') && hasHorizontalCollision(turtle, foot)) {
playSound("strike");
if (this.timeout) {
clearTimeout(this.timeout); //stops foot from 'recovering from punch'
this.timeout = null;
}
if (this.footAttackInterval) {
clearTimeout(this.footAttackInterval); //stops foot from attacking
this.footAttackInterval = null;
}
if (this.footWalkingInterval) {
clearTimeout(this.footWalkingInterval); //stops foot from walking forward
this.footWalkingInterval = null;
}
newFoot = merge({}, foot);
if (newFoot.health > 0) { // to stop reducing health after foot's health is negative
newFoot.health -= TURTLE_ATTACK_DAMAGE;
}
this.setDamageSprite(newFoot); // render foot-hurt or foot-die sprite
this.setState(newFoot); //reduce foot's React health
}- Add option to select any of the 4 turtles
- Jump attack
- Boss level



