Skip to content

Commit

Permalink
episode 11 - slopes
Browse files Browse the repository at this point in the history
  • Loading branch information
mark committed Oct 1, 2015
1 parent 2d4ffc0 commit fd383de
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 2 deletions.
25 changes: 24 additions & 1 deletion maps/Map 1.tmx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" renderorder="right-down" width="20" height="16" tilewidth="16" tileheight="16" nextobjectid="18">
<map version="1.0" orientation="orthogonal" renderorder="right-down" width="20" height="16" tilewidth="16" tileheight="16" nextobjectid="32">
<tileset firstgid="1" name="PrtCave" tilewidth="16" tileheight="16">
<image source="../content/tilesets/PrtCave.png" width="256" height="80"/>
</tileset>
Expand Down Expand Up @@ -665,10 +665,33 @@
<object id="14" x="238.052" y="98.6604" width="11.4651" height="32.2834"/>
<object id="15" x="114.35" y="185.252" width="93.833" height="13.2754"/>
<object id="16" x="206.372" y="161.719" width="7.84456" height="25.6457"/>
<object id="24" x="100.601" y="17.4475" width="44.1755" height="14.6633"/>
<object id="25" x="81.8546" y="32.6676" width="30.069" height="20.046"/>
<object id="26" x="82.2259" y="53.4561" width="12.436" height="5.56834"/>
<object id="27" x="159.997" y="31.3683" width="13.5496" height="5.56834"/>
<object id="28" x="192.108" y="90.3928" width="36.9367" height="5.19712"/>
<object id="29" x="226.075" y="96.5179" width="11.8791" height="4.45467"/>
</objectgroup>
<objectgroup color="#00ff00" name="spawn points">
<object id="17" name="player" x="158.337" y="136.764" width="2.57298" height="2.77091">
<ellipse/>
</object>
</objectgroup>
<objectgroup name="slopes">
<object id="19" x="184.003" y="185.49">
<polyline points="0,0 22.4892,-14.3113"/>
</object>
<object id="20" x="113.721" y="171.704">
<polyline points="0,0 22.3203,13.7164"/>
</object>
<object id="21" x="82.2142" y="111.404">
<polyline points="0,0 31.1847,14.1749"/>
</object>
<object id="22" x="224.383" y="128.309">
<polyline points="0,0 13.6499,-6.29994"/>
</object>
<object id="23" x="159.913" y="65.0994">
<polyline points="0,0 14.1749,-6.61494"/>
</object>
</objectgroup>
</map>
49 changes: 49 additions & 0 deletions notes/episode 11 - slopes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
=====================================================
Remaking Cavestory in C++
Episode 11 - Slopes (and jumping)!
By: Limeoats
Website: www.limeoats.com
Twitter: @Limeoats
Github: https://github.com/Limeoats/cavestory-development
Reddit: http://www.reddit.com/r/Limeoats
=====================================================


=====================================================
Problem
=====================================================
-We need to be able to run up and down slopes
-Currently, we only have rectangular "tile" collisions
-We need to be able to jump

=====================================================
Details
=====================================================
-Polyline slopes
-const float JUMP_SPEED = 0.7f;


=====================================================
Solution
=====================================================
-New Object layer in Tiled "slopes"
-Draw slopes with the Polyline tool
-Utils class: string split function
-Create the slope class
-Create list of slopes in level class
-Parse them out of the .tmx file
-Handle the slope collisions (player and game classes)
-Player jumping


=====================================================
Next time
=====================================================
-Animated Tiled objects
-The health chest
-The save floppy disk





3 changes: 3 additions & 0 deletions source/headers/level.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "globals.h"
#include "tile.h"
#include "rectangle.h"
#include "slope.h"

class Graphics;
struct SDL_Texture;
Expand All @@ -22,6 +23,7 @@ class Level {
void draw(Graphics &graphics);

std::vector<Rectangle> checkTileCollisions(const Rectangle &other);
std::vector<Slope> checkSlopeCollisions(const Rectangle &other);

const Vector2 getPlayerSpawnPoint() const;

Expand All @@ -37,6 +39,7 @@ class Level {
std::vector<Tile> _tileList;
std::vector<Tileset> _tilesets;
std::vector<Rectangle> _collisionRects;
std::vector<Slope> _slopes;

/* void loadMap
* Loads a map
Expand Down
7 changes: 7 additions & 0 deletions source/headers/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "animatedsprite.h"
#include "globals.h"
#include "slope.h"

class Graphics;

Expand All @@ -28,10 +29,16 @@ class Player : public AnimatedSprite {
*/
void stopMoving();

/* void jump
* Starts jumping
*/
void jump();

virtual void animationDone(std::string currentAnimation);
virtual void setupAnimations();

void handleTileCollisions(std::vector<Rectangle> &others);
void handleSlopeCollisions(std::vector<Slope> &others);

const float getX() const;
const float getY() const;
Expand Down
52 changes: 52 additions & 0 deletions source/headers/slope.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef SLOPE_H
#define SLOPE_H

#include "globals.h"

#include <cmath>

class Slope {
public:
Slope() {}
Slope(Vector2 p1, Vector2 p2) :
_p1(p1),
_p2(p2)
{
if (this->_p2.x - this->_p1.x != 0) {
this->_slope = (fabs(this->_p2.y) - fabs(this->_p1.y)) / (fabs(this->_p2.x) - fabs(this->_p1.x));
}
}

const inline float getSlope() const {
return this->_slope;
}

const bool collidesWith(const Rectangle &other) const {
return
(other.getRight() >= this->_p2.x &&
other.getLeft() <= this->_p1.x &&
other.getTop() <= this->_p2.y &&
other.getBottom() >= this->_p1.y) ||
(other.getRight() >= this->_p1.x &&
other.getLeft() <= this->_p2.x &&
other.getTop() <= this->_p1.y &&
other.getBottom() >= this->_p2.y) ||
(other.getLeft() <= this->_p1.x &&
other.getRight() >= this->_p2.x &&
other.getTop() <= this->_p1.y &&
other.getBottom() >= this->_p2.y) ||
(other.getLeft() <= this->_p2.x &&
other.getRight() >= this->_p1.x &&
other.getTop() <= this->_p2.y &&
other.getBottom() >= this->_p1.y);
}

const inline Vector2 getP1() const { return this->_p1; }
const inline Vector2 getP2() const { return this->_p2; }

private:
Vector2 _p1, _p2;
float _slope;
};

#endif
28 changes: 28 additions & 0 deletions source/headers/utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef UTILS_H
#define UTILS_H

class Utils {
public:

//unsigned int split
//split a string <txt> everywehre a certain character <ch> is found
//store the resulting substrings in a vector <strs>
//returns the size of the vector
static unsigned int split(const std::string &txt, std::vector<std::string> &strs, char ch) {
int pos = txt.find(ch);
int initialPos = 0;
strs.clear();
while (pos != std::string::npos) {
strs.push_back(txt.substr(initialPos, pos - initialPos + 1));
initialPos = pos + 1;

pos = txt.find(ch, initialPos);
}
//Add the last one
strs.push_back(txt.substr(initialPos, std::min<int>(pos, txt.size() - (initialPos + 1))));

return strs.size();
}
};

#endif
9 changes: 9 additions & 0 deletions source/src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ void Game::gameLoop() {
this->_player.moveRight();
}

if (input.wasKeyPressed(SDL_SCANCODE_Z) == true) {
this->_player.jump();
}

if (!input.isKeyHeld(SDL_SCANCODE_LEFT) && !input.isKeyHeld(SDL_SCANCODE_RIGHT)) {
this->_player.stopMoving();
}
Expand Down Expand Up @@ -90,4 +94,9 @@ void Game::update(float elapsedTime) {
//Player collided with at least one tile. Handle it.
this->_player.handleTileCollisions(others);
}
//Check slopes
std::vector<Slope> otherSlopes;
if ((otherSlopes = this->_level.checkSlopeCollisions(this->_player.getBoundingBox())).size() > 0) {
this->_player.handleSlopeCollisions(otherSlopes);
}
}
49 changes: 49 additions & 0 deletions source/src/level.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "level.h"
#include "graphics.h"
#include "globals.h"
#include "utils.h"

#include "tinyxml2.h"

Expand Down Expand Up @@ -177,6 +178,44 @@ void Level::loadMap(std::string mapName, Graphics &graphics) {
}
}
//Other objectgroups go here with an else if (ss.str() == "whatever")
else if (ss.str() == "slopes") {
XMLElement* pObject = pObjectGroup->FirstChildElement("object");
if (pObject != NULL) {
while (pObject) {
std::vector<Vector2> points;
Vector2 p1;
p1 = Vector2(std::ceil(pObject->FloatAttribute("x")), std::ceil(pObject->FloatAttribute("y")));

XMLElement* pPolyline = pObject->FirstChildElement("polyline");
if (pPolyline != NULL) {
std::vector<std::string> pairs;
const char* pointString = pPolyline->Attribute("points");

std::stringstream ss;
ss << pointString;
Utils::split(ss.str(), pairs, ' ');
//Now we have each of the pairs. Loop through the list of pairs
//and split them into Vector2s and then store them in our points vector
for (int i = 0; i < pairs.size(); i++) {
std::vector<std::string> ps;
Utils::split(pairs.at(i), ps, ',');
points.push_back(Vector2(std::stoi(ps.at(0)), std::stoi(ps.at(1))));
}
}

for (int i = 0; i < points.size(); i += 2) {
this->_slopes.push_back(Slope(
Vector2((p1.x + points.at(i < 2 ? i : i - 1).x) * globals::SPRITE_SCALE,
(p1.y + points.at(i < 2 ? i : i - 1).y) * globals::SPRITE_SCALE),
Vector2((p1.x + points.at(i < 2 ? i + 1 : i).x) * globals::SPRITE_SCALE,
(p1.y + points.at(i < 2 ? i + 1 : i).y) * globals::SPRITE_SCALE)
));
}

pObject = pObject->NextSiblingElement("object");
}
}
}
else if (ss.str() == "spawn points") {
XMLElement* pObject = pObjectGroup->FirstChildElement("object");
if (pObject != NULL) {
Expand Down Expand Up @@ -221,6 +260,16 @@ std::vector<Rectangle> Level::checkTileCollisions(const Rectangle &other) {
return others;
}

std::vector<Slope> Level::checkSlopeCollisions(const Rectangle &other) {
std::vector<Slope> others;
for (int i = 0; i < this->_slopes.size(); i++) {
if (this->_slopes.at(i).collidesWith(other)) {
others.push_back(this->_slopes.at(i));
}
}
return others;
}

const Vector2 Level::getPlayerSpawnPoint() const {
return this->_spawnPoint;
}
Expand Down
39 changes: 38 additions & 1 deletion source/src/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace player_constants {
const float WALK_SPEED = 0.2f;
const float JUMP_SPEED = 0.7f;

const float GRAVITY = 0.002f;
const float GRAVITY_CAP = 0.8f;
Expand Down Expand Up @@ -57,6 +58,14 @@ void Player::stopMoving() {
this->playAnimation(this->_facing == RIGHT ? "IdleRight" : "IdleLeft");
}

void Player::jump() {
if (this->_grounded) {
this->_dy = 0;
this->_dy -= player_constants::JUMP_SPEED;
this->_grounded = false;
}
}

//void handleTileCollisions
//Handles collisions with ALL tiles the player is colliding with
void Player::handleTileCollisions(std::vector<Rectangle> &others) {
Expand All @@ -66,8 +75,13 @@ void Player::handleTileCollisions(std::vector<Rectangle> &others) {
if (collisionSide != sides::NONE) {
switch (collisionSide) {
case sides::TOP:
this->_y = others.at(i).getBottom() + 1;
this->_dy = 0;
this->_y = others.at(i).getBottom() + 1;
if (this->_grounded) {
this->_dx = 0;
this->_x -= this->_facing == RIGHT ? 1.0f : -1.0f;
}

break;
case sides::BOTTOM:
this->_y = others.at(i).getTop() - this->_boundingBox.getHeight() - 1;
Expand All @@ -86,6 +100,29 @@ void Player::handleTileCollisions(std::vector<Rectangle> &others) {
}
}

//void handleSlopeCollisions
//Handles collisions with ALL slopes the player is colliding with
void Player::handleSlopeCollisions(std::vector<Slope> &others) {
for (int i = 0; i < others.size(); i++) {
//Calculate where on the slope the player's bottom center is touching
//and use y=mx+b to figure out the y position to place him at
//First calculate "b" (slope intercept) using one of the points (b = y - mx)
int b = (others.at(i).getP1().y - (others.at(i).getSlope() * fabs(others.at(i).getP1().x)));

//Now get player's center x
int centerX = this->_boundingBox.getCenterX();

//Now pass that X into the equation y = mx + b (using our newly found b and x) to get the new y position
int newY = (others.at(i).getSlope() * centerX) + b - 8; //8 is temporary to fix a problem

//Re-position the player to the correct "y"
if (this->_grounded) {
this->_y = newY - this->_boundingBox.getHeight();
this->_grounded = true;
}
}
}

void Player::update(float elapsedTime) {
//Apply gravity
if (this->_dy <= player_constants::GRAVITY_CAP) {
Expand Down

0 comments on commit fd383de

Please sign in to comment.