parent
fc3771eae8
commit
ea357b7ee5
@ -0,0 +1,317 @@ |
||||
/*
|
||||
OneLoneCoder.com - Code-It-Yourself! Simple Tile Based Platform Game |
||||
"Its-a meee-a Jario!" - @Javidx9 |
||||
|
||||
Disclaimer |
||||
~~~~~~~~~~ |
||||
I don't care what you use this for. It's intended to be educational, and perhaps |
||||
to the oddly minded - a little bit of fun. Please hack this, change it and use it |
||||
in any way you see fit. BUT, you acknowledge that I am not responsible for anything |
||||
bad that happens as a result of your actions. However, if good stuff happens, I |
||||
would appreciate a shout out, or at least give the blog some publicity for me. |
||||
Cheers! |
||||
|
||||
Background |
||||
~~~~~~~~~~ |
||||
Tile maps are fundamental to most 2D games. This program explores emulating a classic 2D platformer |
||||
using floating point truncation to implement robust collision between a moving tile and a tilemap |
||||
representing the level. |
||||
|
||||
Controls |
||||
~~~~~~~~ |
||||
Left and Right arrow keys move Jario, Space bar jumps.
|
||||
(Up and Down also move jario) |
||||
|
||||
Author |
||||
~~~~~~ |
||||
Twitter: @javidx9 |
||||
Blog: www.onelonecoder.com |
||||
YouTube: www.youtube.com/javidx9 |
||||
Discord: https://discord.gg/WhwHUMV
|
||||
|
||||
Video: |
||||
~~~~~~ |
||||
https://youtu.be/oJvJZNyW_rw
|
||||
|
||||
Last Updated: 04/02/2018 |
||||
*/ |
||||
|
||||
#include <iostream> |
||||
#include <string> |
||||
using namespace std; |
||||
|
||||
#include "olcConsoleGameEngine.h" |
||||
|
||||
class OneLoneCoder_Platformer : public olcConsoleGameEngine |
||||
{ |
||||
public: |
||||
OneLoneCoder_Platformer() |
||||
{ |
||||
m_sAppName = L"Tile Based Platform Game"; |
||||
} |
||||
|
||||
private: |
||||
// Level storage
|
||||
wstring sLevel; |
||||
int nLevelWidth; |
||||
int nLevelHeight; |
||||
|
||||
// Player Properties
|
||||
float fPlayerPosX = 1.0f; |
||||
float fPlayerPosY = 1.0f; |
||||
float fPlayerVelX = 0.0f; |
||||
float fPlayerVelY = 0.0f; |
||||
bool bPlayerOnGround = false; |
||||
|
||||
// Camera properties
|
||||
float fCameraPosX = 0.0f; |
||||
float fCameraPosY = 0.0f; |
||||
|
||||
// Sprite Resources
|
||||
olcSprite *spriteTiles = nullptr; |
||||
olcSprite *spriteMan = nullptr; |
||||
|
||||
// Sprite selection flags
|
||||
int nDirModX = 0; |
||||
int nDirModY = 0; |
||||
|
||||
protected: |
||||
virtual bool OnUserCreate() |
||||
{ |
||||
nLevelWidth = 64; |
||||
nLevelHeight = 16; |
||||
sLevel += L"................................................................"; |
||||
sLevel += L"................................................................"; |
||||
sLevel += L".......ooooo...................................................."; |
||||
sLevel += L"........ooo....................................................."; |
||||
sLevel += L".......................########................................."; |
||||
sLevel += L".....BB?BBBB?BB.......###..............#.#......................"; |
||||
sLevel += L"....................###................#.#......................"; |
||||
sLevel += L"...................####........................................."; |
||||
sLevel += L"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.##############.....########"; |
||||
sLevel += L"...................................#.#...............###........"; |
||||
sLevel += L"........................############.#............###..........."; |
||||
sLevel += L"........................#............#.........###.............."; |
||||
sLevel += L"........................#.############......###................."; |
||||
sLevel += L"........................#................###...................."; |
||||
sLevel += L"........................#################......................."; |
||||
sLevel += L"................................................................"; |
||||
|
||||
spriteTiles = new olcSprite(L"JarioSprites/leveljario.spr"); |
||||
spriteMan = new olcSprite(L"JarioSprites/minijario.spr"); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
virtual bool OnUserUpdate(float fElapsedTime) |
||||
{ |
||||
// Utility Lambdas
|
||||
auto GetTile = [&](int x, int y) |
||||
{ |
||||
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight) |
||||
return sLevel[y * nLevelWidth + x]; |
||||
else |
||||
return L' '; |
||||
}; |
||||
|
||||
auto SetTile = [&](int x, int y, wchar_t c) |
||||
{ |
||||
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight) |
||||
sLevel[y*nLevelWidth + x] = c; |
||||
}; |
||||
|
||||
//fPlayerVelX = 0.0f;
|
||||
//fPlayerVelY = 0.0f;
|
||||
|
||||
// Handle Input
|
||||
if (IsFocused()) |
||||
{ |
||||
if (GetKey(VK_UP).bHeld) |
||||
{ |
||||
fPlayerVelY = -6.0f; |
||||
} |
||||
|
||||
if (GetKey(VK_DOWN).bHeld) |
||||
{ |
||||
fPlayerVelY = 6.0f; |
||||
} |
||||
|
||||
if (GetKey(VK_LEFT).bHeld) |
||||
{ |
||||
fPlayerVelX += (bPlayerOnGround ? -25.0f : -15.0f) * fElapsedTime; |
||||
nDirModY = 1; |
||||
} |
||||
|
||||
if (GetKey(VK_RIGHT).bHeld) |
||||
{ |
||||
fPlayerVelX += (bPlayerOnGround ? 25.0f : 15.0f) * fElapsedTime; |
||||
nDirModY = 0; |
||||
} |
||||
|
||||
if (GetKey(VK_SPACE).bPressed) |
||||
{ |
||||
if (fPlayerVelY == 0) |
||||
{ |
||||
fPlayerVelY = -12.0f; |
||||
nDirModX = 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Gravity
|
||||
fPlayerVelY += 20.0f * fElapsedTime; |
||||
|
||||
// Drag
|
||||
if (bPlayerOnGround) |
||||
{ |
||||
fPlayerVelX += -3.0f * fPlayerVelX * fElapsedTime; |
||||
if (fabs(fPlayerVelX) < 0.01f) |
||||
fPlayerVelX = 0.0f; |
||||
} |
||||
|
||||
// Clamp velocities
|
||||
if (fPlayerVelX > 10.0f) |
||||
fPlayerVelX = 10.0f; |
||||
|
||||
if (fPlayerVelX < -10.0f) |
||||
fPlayerVelX = -10.0f; |
||||
|
||||
if (fPlayerVelY > 100.0f) |
||||
fPlayerVelY = 100.0f; |
||||
|
||||
if (fPlayerVelY < -100.0f) |
||||
fPlayerVelY = -100.0f; |
||||
|
||||
// Calculate potential new position
|
||||
float fNewPlayerPosX = fPlayerPosX + fPlayerVelX * fElapsedTime; |
||||
float fNewPlayerPosY = fPlayerPosY + fPlayerVelY * fElapsedTime; |
||||
|
||||
// Check for pickups!
|
||||
if (GetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 0.0f) == L'o') |
||||
SetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 0.0f, L'.'); |
||||
|
||||
if (GetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 1.0f) == L'o') |
||||
SetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 1.0f, L'.'); |
||||
|
||||
if (GetTile(fNewPlayerPosX + 1.0f, fNewPlayerPosY + 0.0f) == L'o') |
||||
SetTile(fNewPlayerPosX + 1.0f, fNewPlayerPosY + 0.0f, L'.'); |
||||
|
||||
if (GetTile(fNewPlayerPosX + 1.0f, fNewPlayerPosY + 1.0f) == L'o') |
||||
SetTile(fNewPlayerPosX + 1.0f, fNewPlayerPosY + 1.0f, L'.'); |
||||
|
||||
// Check for Collision
|
||||
if (fPlayerVelX <= 0) // Moving Left
|
||||
{ |
||||
if (GetTile(fNewPlayerPosX + 0.0f, fPlayerPosY + 0.0f) != L'.' || GetTile(fNewPlayerPosX + 0.0f, fPlayerPosY + 0.9f) != L'.') |
||||
{ |
||||
fNewPlayerPosX = (int)fNewPlayerPosX + 1; |
||||
fPlayerVelX = 0; |
||||
} |
||||
} |
||||
else // Moving Right
|
||||
{ |
||||
if (GetTile(fNewPlayerPosX + 1.0f, fPlayerPosY + 0.0f) != L'.' || GetTile(fNewPlayerPosX + 1.0f, fPlayerPosY + 0.9f) != L'.') |
||||
{ |
||||
fNewPlayerPosX = (int)fNewPlayerPosX; |
||||
fPlayerVelX = 0; |
||||
|
||||
} |
||||
} |
||||
|
||||
bPlayerOnGround = false; |
||||
if (fPlayerVelY <= 0) // Moving Up
|
||||
{ |
||||
if (GetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY) != L'.' || GetTile(fNewPlayerPosX + 0.9f, fNewPlayerPosY) != L'.') |
||||
{ |
||||
fNewPlayerPosY = (int)fNewPlayerPosY + 1; |
||||
fPlayerVelY = 0; |
||||
} |
||||
} |
||||
else // Moving Down
|
||||
{ |
||||
if (GetTile(fNewPlayerPosX + 0.0f, fNewPlayerPosY + 1.0f) != L'.' || GetTile(fNewPlayerPosX + 0.9f, fNewPlayerPosY + 1.0f) != L'.') |
||||
{ |
||||
fNewPlayerPosY = (int)fNewPlayerPosY; |
||||
fPlayerVelY = 0; |
||||
bPlayerOnGround = true; // Player has a solid surface underfoot
|
||||
nDirModX = 0; |
||||
} |
||||
} |
||||
|
||||
// Apply new position
|
||||
fPlayerPosX = fNewPlayerPosX; |
||||
fPlayerPosY = fNewPlayerPosY; |
||||
|
||||
// Link camera to player position
|
||||
fCameraPosX = fPlayerPosX; |
||||
fCameraPosY = fPlayerPosY; |
||||
|
||||
// Draw Level
|
||||
int nTileWidth = 16; |
||||
int nTileHeight = 16; |
||||
int nVisibleTilesX = ScreenWidth() / nTileWidth; |
||||
int nVisibleTilesY = ScreenHeight() / nTileHeight; |
||||
|
||||
// Calculate Top-Leftmost visible tile
|
||||
float fOffsetX = fCameraPosX - (float)nVisibleTilesX / 2.0f; |
||||
float fOffsetY = fCameraPosY - (float)nVisibleTilesY / 2.0f; |
||||
|
||||
// Clamp camera to game boundaries
|
||||
if (fOffsetX < 0) fOffsetX = 0; |
||||
if (fOffsetY < 0) fOffsetY = 0; |
||||
if (fOffsetX > nLevelWidth - nVisibleTilesX) fOffsetX = nLevelWidth - nVisibleTilesX; |
||||
if (fOffsetY > nLevelHeight - nVisibleTilesY) fOffsetY = nLevelHeight - nVisibleTilesY; |
||||
|
||||
// Get offsets for smooth movement
|
||||
float fTileOffsetX = (fOffsetX - (int)fOffsetX) * nTileWidth; |
||||
float fTileOffsetY = (fOffsetY - (int)fOffsetY) * nTileHeight; |
||||
|
||||
// Draw visible tile map
|
||||
for (int x = -1; x < nVisibleTilesX + 1; x++) |
||||
{ |
||||
for (int y = -1; y < nVisibleTilesY + 1; y++) |
||||
{ |
||||
wchar_t sTileID = GetTile(x + fOffsetX, y + fOffsetY); |
||||
switch (sTileID) |
||||
{ |
||||
case L'.': // Sky
|
||||
Fill(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, (x + 1) * nTileWidth - fTileOffsetX, (y + 1) * nTileHeight - fTileOffsetY, PIXEL_SOLID, FG_CYAN); |
||||
break; |
||||
case L'#': // Solid Block
|
||||
//Fill(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, (x + 1) * nTileWidth - fTileOffsetX, (y + 1) * nTileHeight - fTileOffsetY, PIXEL_SOLID, FG_RED);
|
||||
DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 2 * nTileWidth, 0 * nTileHeight, nTileWidth, nTileHeight); |
||||
break; |
||||
case L'G': // Ground Block
|
||||
DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 0 * nTileWidth, 0 * nTileHeight, nTileWidth, nTileHeight); |
||||
break; |
||||
case L'B': // Brick Block
|
||||
DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 0 * nTileWidth, 1 * nTileHeight, nTileWidth, nTileHeight); |
||||
break; |
||||
case L'?': // Question Block
|
||||
DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 1 * nTileWidth, 1 * nTileHeight, nTileWidth, nTileHeight); |
||||
break; |
||||
case L'o': // Coin
|
||||
Fill(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, (x + 1) * nTileWidth - fTileOffsetX, (y + 1) * nTileHeight - fTileOffsetY, PIXEL_SOLID, FG_CYAN); |
||||
DrawPartialSprite(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, spriteTiles, 3 * nTileWidth, 0 * nTileHeight, nTileWidth, nTileHeight); |
||||
break; |
||||
default: |
||||
Fill(x * nTileWidth - fTileOffsetX, y * nTileHeight - fTileOffsetY, (x + 1) * nTileWidth - fTileOffsetX, (y + 1) * nTileHeight - fTileOffsetY, PIXEL_SOLID, FG_BLACK); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Draw Player
|
||||
//Fill((fPlayerPosX - fOffsetX) * nTileWidth, (fPlayerPosY - fOffsetY) * nTileWidth, (fPlayerPosX - fOffsetX + 1.0f) * nTileWidth, (fPlayerPosY - fOffsetY + 1.0f) * nTileHeight, PIXEL_SOLID, FG_GREEN);
|
||||
DrawPartialSprite((fPlayerPosX - fOffsetX) * nTileWidth, (fPlayerPosY - fOffsetY) * nTileWidth, spriteMan, nDirModX * nTileWidth, nDirModY * nTileHeight, nTileWidth, nTileHeight); |
||||
return true; |
||||
} |
||||
}; |
||||
|
||||
int main() |
||||
{ |
||||
OneLoneCoder_Platformer game; |
||||
if (game.ConstructConsole(256, 240, 4, 4)) |
||||
game.Start(); |
||||
return 0; |
||||
} |
Loading…
Reference in new issue