Hi I made a level generator with a 3D Buzz tutorial called Evil Monkeys.
I generated a level but I can't get it to draw on the screen.
My code:
Level.cpp
#include "Level.h"
#include <stdlib.h>
#include "Sprite.h"
Level::Level(drawEngine *de, int w, int h)
{
drawArea = de;
width = w;
height = h;
gamer = 0;
//create memory for our level
level = new char *[width];
for(int x = 0; x < width; x++)
{
level[x] = new char[height];
}
//create the level
createLevel();
drawArea->setMap(level);
}
Level::~Level()
{
for(x = 0; x < width; x++)
delete []level[x];
delete [] level;
}
void Level::createLevel(void)
{
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
int random = rand() % 100;
if (y == 0 || y == height - 1 || x == 0 || x == width - 1)
{
level[x][y] = TILE_WALL;
}
else
{
if (random < 90 || (x < 3 && y < 3))
level[x][y] = TILE_EMPTY;
else
level[x][y] = TILE_WALL;
}
}
}
}
void Level::draw(void)
{
// level to be drawn here
drawArea->drawBackground();
}
Level.h
#ifndef LEVEL_H
#define LEVEL_H
enum
{
TILE_EMPTY,
TILE_WALL
};
#include "drawEngine.h"
class Character;
class Level
{
public:
Level(drawEngine *de, int width = 30, int height = 20);
~Level();
int x;
int y;
void addPlayer(Character *p);
void update(void);
void draw(void);
bool keyPress(char c);
protected:
void createLevel(void);
private:
int width;
int height;
char **level;
Character *gamer;
drawEngine *drawArea;
};
#endif
Game.cpp
#include "Game.h"
#include <conio.h>
#include <iostream>
#include "drawEngine.h"
#include "Character.h"
#include <windows.h>
using namespace std;
//this will give ME 32 fps
#define GAME_SPEED 25.33
bool Runner::run()
{
level = new Level(&drawArea, 30, 20);
drawArea.createBackgroundTile(TILE_EMPTY, ' ');
drawArea.createBackgroundTile(TILE_WALL, '+');
drawArea.createSprite(0, '$');
gamer = new Character(&drawArea, 0);
level->draw();
char key = ' ';
startTime = timeGetTime();
frameCount = 0;
lastTime = 0;
posX = 0;
while (key != 'q')
{
while(!getInput(&key))
{
timerUpdate();
}
//gamer->keyPress(key);
//cout << "Here's what you pressed: " << key << endl;
}
delete gamer;
cout << frameCount / ((timeGetTime() - startTime) / 100) << " fps " << endl;
cout << "Game Over" << endl;
return true;
}
bool Runner::getInput(char *c)
{
if (kbhit())
{
*c = getch();
return true;
}
}
void Runner::timerUpdate()
{
double currentTime = timeGetTime() - lastTime;
if (currentTime < GAME_SPEED)
return;
frameCount++;
lastTime = timeGetTime();
}
game.h
#ifndef GAME_H
#define GAME_H
#include "drawEngine.h"
#include "Sprite.h"
#include "Character.h"
#include "level.h"
class Runner
{
public:
bool run();
Runner(){};
protected:
bool getInput(char *c);
void timerUpdate();
private:
Level *level;
Character* gamer;
double frameCount;
double startTime;
double lastTime;
int posX;
drawEngine drawArea;
};
#endif
drawEngine.cpp
#include <iostream>
#include "drawEngine.h"
#include <windows.h>
using namespace std;
drawEngine::drawEngine(int index, int xSize, int ySize, int x, int y)
{
screenWidth = x;
screenHeight = y;
map = 0;
//set cursor visibility to false
//cursorVisibility(false);
}
drawEngine::~drawEngine()
{
cursorVisibility(true);
//set cursor visibility to true
}
int drawEngine::createSprite(int index, char c)
{
if (index >= 0 && index < 16)
{
spriteImage[index] = c;
return index;
}
return -1;
}
void drawEngine::deleteSprite(int index)
{
// in this implemantation we don't need it
}
void drawEngine::drawSprite(int index, int posX, int posY)
{
//go to the correct location
gotoxy (index, posX, posY);
// draw the sprite
cout << spriteImage[index];
cursorVisibility(false);
}
void drawEngine::eraseSprite(int index, int posX, int posY)
{
gotoxy (index, posX, posY);
cout << ' ';
}
void drawEngine::setMap(char **data)
{
map = data;
}
void drawEngine::createBackgroundTile(int index, char c)
{
if (index >= 0 && index < 16)
{
tileImage[index] = c;
}
}
void drawEngine::drawBackground(void)
{
if(map)
{
for(int y = 0; y < screenHeight; y++)
{
gotoxy(0,y, 0);
for(int x = 0; x < screenWidth; x++)
cout << tileImage[map[x][y]];
}
}
}
void drawEngine::gotoxy(int index, int x, int y)
{
HANDLE output_handle;
COORD pos;
pos.X = x;
pos.Y = y;
output_handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(output_handle, pos);
}
void drawEngine::cursorVisibility(bool visibility)
{
HANDLE output_handle;
CONSOLE_CURSOR_INFO cciInfo;
cciInfo.dwSize = 1;
cciInfo.bVisible = visibility;
output_handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(output_handle, &cciInfo);
}
drawEngine.h
#ifndef DRAWENGINE_H
#define DRAWENGINE_H
class drawEngine
{
public:
drawEngine(int index, int xSize = 30, int ySize = 20, int x = 0, int y = 0);
~drawEngine();
drawEngine(){};
int createSprite(int index, char c);
void deleteSprite(int index);
void eraseSprite(int index, int posX, int posY);
void createBackgroundTile(int index, char c);
void drawSprite(int index, int posX, int posY);
void drawBackground(void);
void setMap(char **);
protected:
char **map;
int screenWidth, screenHeight;
char spriteImage[16];
char tileImage[16];
private:
void gotoxy(int index, int x, int y);
void cursorVisibility(bool visibility);
};
#endif
I've also got Sprite.cpp, Sprite.h, Character.h,Character.cpp and main.cpp if you need them
Ok, I made it through the code and found one issue. The Runner class encapsulates a drawEngine object. At the constructor of Runner, the default c'tor of drawEngine is called, which doesn't set values for sceenWidth and screenHeight (or any other member). Luckily in debug mode, they are defaulted to 0xcccccccc which is negative so you're drawBackground returns immediately (Visual Studio 2010).
You should change that c'tor (or even remove it) and corretly initialize the engine in runner's constructor, e.g.:
class Runner {
public:
Runner() : drawArea(0, width, height, ?, ?){};
[...]
};
Further, the x and y members are used in the loops in drawBackground. You should use screenWidth and screenWidth, resp. BTW, I don't know what x and y should be in drawEngine
UPDATE: The x and y coordinates at the gotoxy call in drawBackground are mixed up, so you draw everything on the same line. BTW: what is index used for?
Related
As a school project, I've made the classic snake game using SDL2 and C++.
I've already implemented the growing, moving features for the Snake but it was required to make the movement based on a grid, but when I implemented the grid feature, the self-collision was always triggering whenever grow one part, so every time I start the game, and eat the first fruit, the snake dies.
I've been trying for a while now, from placing a delay to the adding of the tail and delaying the collision check, but to no avail, it's always colliding with itself even though it is not.
I can't see what is wrong with the self collision, I would gladly appreciate it if someone can point out what's wrong.
Snake.h
#include "GameObject.h"
#include "common.h"
#include "draw.h"
#include "Food.h"
#include "util.h"
#include <vector>
struct Segment {
int x;
int y;
Segment(int posx, int posy) {
x = posx;
y = posy;
}
};
class Snake :
public GameObject
{
public:
~Snake();
void start();
void update();
void draw();
void outOfBoundsCheck();
void move();
void addSegment(int x, int y);
void selfCollisionCheck(bool hasEaten);
void setHasMoved(bool a);
void setIsAlive(bool a);
int getX();
int getY();
int getWidth();
int getHeight();
bool getIsAlive();
bool getHasMoved();
std::vector<Segment*> const& getV() const;
private:
std::vector<Segment*> body;
SDL_Texture* headTexture;
SDL_Texture* bodyTexture;
int x;
int y;
int width;
int height;
int dx;
int dy;
int tempX;
int tempY;
bool isAlive;
bool hasMoved;
};
Snake.cpp
Snake::~Snake()
{
}
void Snake::start()
{
// Load Texture
headTexture = loadTexture("gfx/player.png");
bodyTexture = loadTexture("gfx/body.png");
tempX = 0;
tempY = 0;
x = 0;
y = 0;
dx = 0;
dy = 0;
isAlive = true;
hasMoved = false;
width = 0;
height = 0;
SDL_QueryTexture(headTexture, NULL, NULL, &width, &height);
addSegment(x, y);
}
void Snake::update()
{
std::cout << "Head" << body[0]->x << std::endl;
if (body.size() > 1) {
std::cout << "2nd Segment" << body[1]->x << std::endl;
if (body.size() > 2) {
std::cout << "3nd Segment" << body[2]->x << std::endl;
}
}
move();
outOfBoundsCheck();
}
void Snake::draw()
{
if (!isAlive) return; // Cancel the render if player dies
for (int i = 0; i < body.size(); i++) {
blit(headTexture, body[i]->x, body[i]->y);
}
}
void Snake::outOfBoundsCheck()
{
for (int i = 0; i < body.size(); i++) {
if (body[i]->x > SCREEN_WIDTH) {
body[i]->x = 0;
}
if (body[i]->x < 0) {
body[i]->x = SCREEN_WIDTH;
}
if (body[i]->y > SCREEN_HEIGHT) {
body[i]->y = 0;
}
if (body[i]->y < 0) {
body[i]->y = SCREEN_HEIGHT;
}
}
}
void Snake::move()
{
if (app.keyboard[SDL_SCANCODE_W] && dy != 5) {
dx = 0;
dy = -5;
}
if (app.keyboard[SDL_SCANCODE_A] && dx != 5) {
dx = -5;
dy = 0;
}
if (app.keyboard[SDL_SCANCODE_S] && dy != -5) {
dx = 0;
dy = 5;
}
if (app.keyboard[SDL_SCANCODE_D] && dx != -5) {
dx = 5;
dy = 0;
}
Segment* snakeHead = *(body.begin()); //Grid
tempX += dx;
tempY += dy;
if (tempX % 25 == 0) {
snakeHead->x += tempX;
tempX = 0;
}
if (tempY % 25 == 0) {
snakeHead->y += tempY;
tempY = 0;
}
for (int i = body.size() - 1; i > 0; i--) { //For the other parts to follow
body[i]->x = body[i - 1]->x;
body[i]->y = body[i - 1]->y;
}
}
void Snake::addSegment(int x, int y)
{
Segment* seg = new Segment(x, y );
body.push_back(seg);
}
void Snake::selfCollisionCheck(bool hasEaten) // Fail
{
Segment* head = body[0];
if (hasEaten == false) {
for (int i = 1; i < body.size(); i++) {
if (head->x == body[i]->x && head->y == body[i]->y) {
isAlive = false;
break;
}
}
}
else {
return;
}
}
void Snake::setHasMoved(bool a)
{
hasMoved = a;
}
void Snake::setIsAlive(bool a)
{
isAlive = a;
}
int Snake::getX()
{
return x;
}
int Snake::getY()
{
return y;
}
int Snake::getWidth()
{
return width;
}
int Snake::getHeight()
{
return height;
}
bool Snake::getIsAlive()
{
return isAlive;
}
bool Snake::getHasMoved()
{
return hasMoved;
}
std::vector<Segment*> const& Snake::getV() const
{
// TODO: insert return statement here
return body;
}
GameScene.h
#include "Scene.h"
#include "GameObject.h"
#include "Snake.h"
#include "Food.h"
#include "util.h"
#include "text.h"
#include "SoundManager.h"
class GameScene : public Scene
{
public:
GameScene();
~GameScene();
void start();
void draw();
void update();
void spawnFood();
void collisionLogic();
void selfCollision();
void despawnFood(Food* food);
private:
Snake* snake;
Food* food;
int points;
std::vector<Food*> spawnedFood;
};
GameScene.cpp
#include "GameScene.h"
GameScene::GameScene()
{
// Register and add game objects on constructor
snake = new Snake();
this->addGameObject(snake);
points = 0;
}
GameScene::~GameScene()
{
delete snake;
delete food;
}
void GameScene::start()
{
Scene::start();
// Initialize any scene logic here
initFonts();
spawnFood();
}
void GameScene::draw()
{
Scene::draw();
drawText(110, 20, 255, 255, 255, TEXT_CENTER, "POINTS: %03d", points);
if (snake->getIsAlive() == false) {
drawText(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 255, 255, 255, TEXT_CENTER, "GAME OVER!");
}
}
void GameScene::update()
{
Scene::update();
if (spawnedFood.size() == 0 && spawnedFood.size() != 1) {
spawnFood();
}
collisionLogic();
selfCollision();
}
void GameScene::spawnFood()
{
int random = rand() % 720;
if (random % 25 != 0) {
random = rand() % 720;
}
else {
Food* food = new Food();
this->addGameObject(food);
food->setPosition(rand() % SCREEN_WIDTH, rand() % SCREEN_HEIGHT);
spawnedFood.push_back(food);
}
}
void GameScene::collisionLogic()
{
Segment* head = snake->getV()[0];
std::vector<Segment*> snakeBody = snake->getV();
for (int i = 0; i < objects.size(); i++) {
Food* food = dynamic_cast<Food*>(objects[i]);
if (food != NULL) {
int collision = checkCollision(
head->x, head->y, snake->getWidth(), snake->getHeight(),
food->getX(), food->getY(), food->getWidth(), food->getHeight()
);
if (collision == 1) {
despawnFood(food);
snake->addSegment(snakeBody[snakeBody.size() - 1]->x, snakeBody[snakeBody.size() - 1]->y); //Adds a part to the snake
points++;
break;
}
}
}
}
void GameScene::selfCollision()
{
std::vector<Segment*> body = snake->getV();
Segment* head = snake->getV()[0];
for (int i = 1; i < snake->getV().size(); i++) {
if (head->x == body[i]->x && head->y == body[i]->y) {
snake->setIsAlive(false);
break;
}
}
}
void GameScene::despawnFood(Food* food)
{
int index = -1;
for (int i = 0; i < spawnedFood.size(); i++) {
if (food == spawnedFood[i]) {
index = i;
break;
}
}
if (index != -1) {
spawnedFood.erase(spawnedFood.begin() + index);
delete food;
}
}
It seems that I had some logical errors when it comes to the grid movement because when I re-coded everything and changed the grid movement into cell based instead of using modulo condition by dividing the screen width and height to the pixel size of my snake and using that as the coordinates for my movement, everything went back to normal and the collision bug disappeared.
Instead of doing this for the grid movement
Old Grid Movement Code
tempX += dx;
tempY += dy;
if (tempX % 25 == 0) {
snakeHead->x += tempX;
tempX = 0;
}
if (tempY % 25 == 0) {
snakeHead->y += tempY;
tempY = 0;
}
I defined this as a permanent value in my defs.h
defs.h
#define CELL_SIZE 25 // Size of the segment
#define CELL_WIDTH SCREEN_WIDTH / CELL_SIZE
#define CELL_HEIGHT SCREEN_HEIGHT / CELL_SIZE
After that, since I'm still going to render the picture with the default resolution, I multiplied CELL_SIZE to the dest variable of my blit function
draw.cpp
void blit(SDL_Texture* texture, int x, int y)
{
SDL_Rect dest;
dest.x = x * CELL_SIZE;
dest.y = y * CELL_SIZE;
SDL_QueryTexture(texture, NULL, NULL, &dest.w, &dest.h);
SDL_RenderCopy(app.renderer, texture, NULL, &dest);
}
This results to the snake and any other thing that I'm going to render to follow a grid and by assigning the x and y values with the CELL_WIDTH and CELL_HEIGHT as substitution to the resolution, I accomplished the grid movement with no conflict with my collision check.
I am creating a classic breakout game in C++ using SFML library. So far I have a movable paddle and ball implemented.
Currently, my goal is to create a layout of bricks. So far I have it so that a single brick is displayed. However, I want to make it so that I can draw a layout of bricks in text file using a letter (For example using letter 'B'), read that text file's brick layout and draw that layout in game.
I am not sure how to do this so any help would be great!
Here is my code so far (Note that I did not include for Paddle and Ball as I thought it was not needed for this problem):
GameObject.h
#pragma once
#include <SFML/Graphics.hpp>
class GameObject
{
protected:
sf::Vector2f position;
float speed;
sf::RenderWindow& m_window;
public:
GameObject(float startX, float startY, sf::RenderWindow& window);
virtual ~GameObject() {};
virtual void Draw() = 0;
virtual void Update() = 0;
};
GameObject.cpp
#include "GameObject.h"
GameObject::GameObject(float startX, float startY, sf::RenderWindow& window)
: position{ startX, startY }
, speed{ 0.5f }
, m_window{ window }
{
}
Brick.h
#pragma once
#include "GameObject.h"
class Brick : public GameObject
{
private:
sf::RectangleShape brickShape;
static constexpr int shapeWidth = 50;
static constexpr int shapeHeight = 20;
int color;
int strength;
public:
Brick(float startX, float startY, sf::RenderWindow& window);
sf::FloatRect getPosition();
sf::RectangleShape getShape();
int getStrength();
void setStrength(int strengthValue);
void Draw() override;
void Update() override;
};
Brick.cpp
#include "Brick.h"
Brick::Brick(float startX, float startY, sf::RenderWindow& window)
: GameObject{ startX, startY, window }
{
brickShape.setSize(sf::Vector2f(shapeWidth, shapeHeight));
brickShape.setPosition(position);
color = (rand() % 2) + 1;
if (color == 1)
{
brickShape.setFillColor(sf::Color::Yellow);
strength = 1;
}
else
{
brickShape.setFillColor(sf::Color(255, 165, 0));
strength = 2;
}
}
sf::FloatRect Brick::getPosition()
{
return brickShape.getGlobalBounds();
}
sf::RectangleShape Brick::getShape()
{
return brickShape;
}
int Brick::getStrength()
{
return strength;
}
void Brick::setStrength(int strengthValue)
{
strength = strengthValue;
}
void Brick::Draw()
{
m_window.draw(brickShape);
}
void Brick::Update()
{
}
Game.h
#pragma once
#include <SFML/Graphics.hpp>
#include "Paddle.h"
#include "Ball.h"
#include "Brick.h"
#include <algorithm>
#include <fstream>
#include <iostream>
class Game
{
private:
sf::RenderWindow& m_window;
std::unique_ptr<Paddle> player;
std::unique_ptr<Ball> ball;
std::unique_ptr<Brick> brick;
std::vector<std::unique_ptr<Brick>>bricks;
int bricksCountX;
int bricksCountY;
public:
const unsigned int m_windowWidth;
const unsigned int m_windowHeight;
public:
Game(sf::RenderWindow& window, const unsigned int& windowWidth, const unsigned int& windowHeight);
float RandomFloat(float a, float b);
void ReadFile();
void HandleCollision();
void HandleInput();
void Draw();
void Update();
void Run();
};
Game.cpp
#include "Game.h"
Game::Game(sf::RenderWindow& window, const unsigned int& windowWidth, const unsigned int& windowHeight)
: m_window{ window }
, m_windowWidth{ windowWidth }
, m_windowHeight{ windowHeight }
{
player = std::make_unique<Paddle>(m_windowWidth/2, m_windowHeight - 70, m_window);
ball = std::make_unique<Ball>(m_windowWidth / 2, m_windowHeight / 2, m_window);
ReadFile();
for (int i = 0; i < bricksCountX; i++)
for (int j = 0; j < bricksCountY; j++)
bricks.emplace_back(std::make_unique<Brick>((i + 1.5) * ((long long)brick->getShape().getSize().x + 3) + 22,
(j + 5) * (brick->getShape().getSize().y + 3), m_window));
}
float Game::RandomFloat(float a, float b)
{
return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}
void Game::ReadFile()
{
// Create a text string, which is used to output the text file
std::string bricksText;
int numOfLines = 0;
// Read from the text file
std::ifstream MyReadFile("Bricks Layout.txt");
// Use a while loop together with the getline() function to read the file line by line
while (std::getline(MyReadFile, bricksText))
{
++numOfLines;
// Output the text from the file
bricksCountX = bricksText.length();
std::cout << bricksCountX;
}
bricksCountY = numOfLines;
// Close the file
MyReadFile.close();
}
void Game::HandleCollision()
{
if (ball->getShape().getPosition().x - ball->getRadius() < 0.0f)
{
ball->reboundLeft();
}
else if (ball->getShape().getPosition().x + ball->getRadius() > m_windowWidth)
{
ball->reboundRight();
}
else if (ball->getShape().getPosition().y - ball->getRadius() < 0.0f)
{
ball->reboundTop();
}
else if (ball->getShape().getPosition().y + ball->getRadius() > m_windowHeight)
{
ball->ballAngle = ball->ballAngle * -1;
}
else if (ball->getPosition().intersects(player->getPosition()))
{
ball->reboundPaddle(*player);
}
for (unsigned int i = 0; i < bricks.size(); i++)
{
if (ball->getPosition().intersects(bricks[i]->getPosition()))
{
if (bricks[i]->getStrength() == 1)
{
ball->reboundBrick(*bricks[i]);
bricks.erase(std::remove(bricks.begin(), bricks.end(), bricks[i]), bricks.end());
}
else
{
ball->reboundBrick(*bricks[i]);
bricks[i]->setStrength(1);
}
}
}
}
void Game::HandleInput()
{
player->HandleInput();
}
void Game::Draw()
{
player->Draw();
ball->Draw();
for (unsigned int i = 0; i < bricks.size(); i++)
{
bricks[i]->Draw();
}
}
void Game::Update()
{
player->Update();
ball->Update();
brick->Update();
}
void Game::Run()
{
//Game Loop
while (m_window.isOpen())
{
sf::Event event;
while (m_window.pollEvent(event))
{
if (event.type == sf::Event::Closed || sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
m_window.close();
}
m_window.clear();
Draw();
HandleInput();
HandleCollision();
Update();
m_window.display();
}
}
I have a base class cGameObject which has a virtual update function. I wish to have many derived game objects each with their own specific update function.
I want to add the derived gameobjects to a vector and iterate through them to call each of their update methods.
What is wrong with my iterator?
//In Header file
std::vector <cGameObject*> vGameObjs;
std::vector <cGameObject*>::iterator Iter;
cGameObject *AGameObj;
/In cpp file - add object pointer to vector
AGameObj = new BrickBat(128, 32, (1024 - 128) / 2, 768 - 64, 0);
vGameObjs.push_back(AGameObj);
AGameObj = new BrickBall(64, 512, 384, 1, 1, 0);
vGameObjs.push_back(AGameObj);
//Iterator Crashing??
for (Iter = vGameObjs.begin(); Iter != vGameObjs.end(); ++Iter)
{
//Call Each Objects Update() method here?
}
When I run this, it throws an exception: read access violation. _Mycont was nullptr.
Not sure what to do.
error thrown
Header of Class App Class:
#ifndef _H_AGK_TEMPLATE_
#define _H_AGK_TEMPLATE_
// Link to GDK libraries
#include "AGK.h"
#include "Brickbat.h"
#include "BrickBall.h"
#include "cGameObject.h"
#include <vector>
#define DEVICE_WIDTH 1024
#define DEVICE_HEIGHT 768
#define DEVICE_POS_X 32
#define DEVICE_POS_Y 32
#define FULLSCREEN false
// used to make a more unique folder for the write path
#define COMPANY_NAME "BitMaNip:Play"
// Global values for the app
class app
{
public:
// constructor
app() { memset ( this, 0, sizeof(app)); }
// main app functions - mike to experiment with a derived class for this..
void Begin( void );
int Loop( void );
void End( void );
private:
//Vector of GameObject Pointers;
std::vector <cGameObject*> vGameObjs;
//Iterator of GameObjects
std::vector <cGameObject*>::iterator Iter;
BrickBat *MyBat;
BrickBall *MyBall;
cGameObject *AGameObj;
};
extern app App;
#endif
// Allow us to use the LoadImage function name
#ifdef LoadImage
#undef LoadImage
#endif
cpp file for Header
// Includes
#include "template.h"
#include "cGameObject.h"
#include
// Namespace
using namespace AGK;
app App;
void app::Begin(void)
{
agk::SetVirtualResolution (DEVICE_WIDTH, DEVICE_HEIGHT);
agk::SetClearColor( 151,170,204 ); // light blue
agk::SetSyncRate(60,0);
agk::SetScissor(0,0,0,0);
//Test
/*BrickBall = agk::CreateSprite(0);
agk::SetSpriteSize(BrickBall, 64, 64);
agk::SetSpriteColor(BrickBall, 255, 255, 255, 255);
xPos = (DEVICE_WIDTH - 64) / 2;
yPos = (DEVICE_HEIGHT - 64) / 2;
agk::SetSpritePosition(BrickBall, xPos , yPos );
xDir = 1;
yDir = 1;
iSpeed = 8;*/
MyBat = new BrickBat(128, 32, (1024-128)/2, 768-64, 0);
AGameObj = new BrickBat(128, 32, (1024 - 128) / 2, 768 - 64, 0);
vGameObjs.push_back(AGameObj);
MyBall = new BrickBall(64, 512, 384, 1, 1, 0);
AGameObj = new BrickBall(64, 512, 384, 1, 1, 0);
vGameObjs.push_back(AGameObj);
}
int app::Loop (void)
{
agk::Print( agk::ScreenFPS() );
if (agk::GetRawKeyState(37) == 1)
{
MyBat->MoveLeft();
}
if (agk::GetRawKeyState(39) == 1)
{
MyBat->MoveRight();
}
MyBat->Update();
MyBall->Update();
MyBall->Collided(MyBat->iGetBatID());
for (Iter = vGameObjs.begin(); Iter != vGameObjs.end(); ++Iter)
{
//(*Iter)->Update();
//(*Iter)->Collided(MyBat->iGetBatID());
}
Derived class
include "cGameObject.h"
class BrickBall: public cGameObject
{
private:
bool bHoriDir;
bool bVertDir;
int iSpeed;
bool bPause; //in case game is paused
public:
BrickBall(int iSize, int xPos, int yPos, bool bHori, bool bVert, int ImageID);
virtual void Update() override;
virtual void Collided(int OtherSpriteToCheck) override;
};
agk::Sync();
return 0; // return 1 to close app
}
void app::End (void)
{
}
Base Class:
#pragma once
class cGameObject
{
protected:
bool bInit = false;
int iSprID;
int iImageID;
int iXPos;
int iYPos;
int iAngle;
int iAlpha;
int iWidth;
int iHeight;
int iColour;
//Movement
float fDeltaX;
float fDeltaY;
//Animation
int iAniType; //Type of animation
//0 = No Animation
//1 = Repeating Loop of Frames (All image is animation)
//2 =
int iFrameW; //Width of Animation Frame
int iFrameH; //Height of Animation frame
int iFPS; //Animation Delay
int iNumFrames; //Number of animation frames
int iAniCount; //Frame Counter
public:
// set up default for constructor
cGameObject(int width = 16, int height = 16, int xPos = 0, int yPos = 0, int ImageID = 0);
void SetPosition(int ixPos, int iyPos);
void SetSize(int iWidth, int iHeight);
void SetWidth(int Width);
void SetAngle(int iAngle);
void SetTransparency(int iAlpha);
void SetAnimation(int Type, int FrameW, int FrameH, int FPS, int NumFrames);
virtual void Collided(int OtherSpriteToCheck) {};
virtual void Update() {};
int iGetWidth();
int iGetHeight();
int iGetX();
int iGetY();
int iGetSprID();
~cGameObject();
};
Base Class cpp
#include "cGameObject.h"
#include "agk.h"
void cGameObject::SetAngle(int iAngle)
{
if (bInit == true)
{
if (iAngle > 0 && iAngle < 359)
{
agk::SetSpriteAngle(iSprID, iAngle);
}
}
}
//cGameObject::cGameObject(int width, int height, int xPos, int yPos, const char * szImageFile)
cGameObject::cGameObject(int width, int height, int xPos, int yPos, int ImageID)
{
bInit = true;
iImageID = 0;
/*if (std::strlen(szImageFile) > 0)
{
iImageID = agk::LoadImage(szImageFile);
if (iImageID < 1)
{
bInit = false;
}
}*/
iColour = agk::MakeColor(255, 255, 255);
//init animation code
iAniType = 0; //Type of animation
iFrameW = 64; //Width of Animation Frame
iFrameH = 64; //Height of Animation frame
iFPS = 10; //Animation Delay
iNumFrames = 1; //Number of animation frames
iAniCount = 0; //Frame Counter
iSprID = agk::CreateSprite(iImageID);
if (iSprID < 1)
{
bInit = false;
}
else
{
agk::SetSpriteSize(iSprID, width, height);
agk::SetSpritePosition(iSprID, xPos, yPos);
fDeltaX = 4.0;
fDeltaY = 4.0;
}
}
void cGameObject::SetPosition(int ixPos, int iyPos)
{
if (bInit == true)
{
agk::SetSpritePosition(iSprID, ixPos, iyPos);
}
}
void cGameObject::SetSize(int iWidth, int iHeight)
{
if (bInit == true)
{
if (iWidth > 0 && iWidth < 1024 && iHeight > 0 && iHeight < 1024)
{
agk::SetSpriteSize(iSprID, iWidth, iHeight);
}
}
}
void cGameObject::SetWidth(int Width)
{
if (bInit == true)
{
agk::GetSpriteWidth(Width);
}
}
void cGameObject::SetTransparency(int iAlpha)
{
if (bInit == true)
{
if (iAlpha > 0 && iAlpha < 256)
{
agk::SetSpriteTransparency(iSprID, iAlpha);
}
}
}
void cGameObject::SetAnimation(int Type, int FrameW, int FrameH, int FPS, int NumFrames)
{
//Animation
iAniType = Type;
iFrameW = FrameW; //Width of Animation Frame
iFrameH = FrameH; //Height of Animation frame
iFPS = FPS; //Animation Delay
iNumFrames = NumFrames; //Number of animation frames
iAniCount = 0; //Frame Counter
agk::SetSpriteAnimation(iSprID, iFrameW, iFrameH, iNumFrames);
if (iAniType > 0)
{
agk::PlaySprite(iSprID, iFPS);
}
}
int cGameObject::iGetWidth()
{
if (bInit == true)
{
return agk::GetSpriteWidth(iSprID);
}
else
{
return 0;
}
}
int cGameObject::iGetHeight()
{
if (bInit == true)
{
return agk::GetSpriteHeight(iSprID);
}
else
{
return 0;
}
}
int cGameObject::iGetX()
{
if (bInit == true)
{
return agk::GetSpriteX(iSprID);
}
else
{
return 0;
}
}
int cGameObject::iGetY()
{
if (bInit == true)
{
return agk::GetSpriteY(iSprID);
}
else
{
return 0;
}
}
int cGameObject::iGetSprID()
{
if (bInit == true)
{
return iSprID;
}
else
{
return 0;
}
}
cGameObject::~cGameObject()
{
if (bInit == true)
{
agk::DeleteSprite(iSprID);
}
}
Derived Class Header:
#include "cGameObject.h"
class BrickBall: public cGameObject
{
private:
bool bHoriDir;
bool bVertDir;
int iSpeed;
bool bPause; //in case game is paused
public:
BrickBall(int iSize, int xPos, int yPos, bool bHori, bool bVert, int ImageID);
virtual void Update() override;
virtual void Collided(int OtherSpriteToCheck) override;
};
Derived Class cpp
#include "BrickBall.h"
BrickBall::BrickBall(int Size, int xPos, int yPos, bool bHori, bool bVert, int ImageID):cGameObject(Size, Size, xPos, yPos, ImageID)
{
iWidth = Size;
iHeight = Size;
iXPos = xPos;
iYPos = yPos;
bHoriDir = bHori;
bVertDir = bVert;
/*iSprID = agk::CreateSprite(0);
agk::SetSpriteColor(iSprIdx, 255, 255, 255, 255);
//agk::SetSpriteSize(iSprIdx, iSize, iSize);
agk::SetSpriteSize(iSprIdx, 64, 64);
//agk::SetSpritePosition(iSprIdx, ixPos, iyPos);
agk::SetSpritePosition(iSprIdx, ixPos, iyPos);
bInit = true;*/
iSpeed = 8;
}
void BrickBall::Update()
{
if (bInit==true)// && BatID > 0)
{
//Move Ball
agk::SetSpriteColor(iSprID, 100, 100, 100, 255);
agk::PrintC("BallX:");
agk::Print(iXPos);
if (bHoriDir == 1) //Right
{
iXPos = iXPos + iSpeed;
if (iXPos > 1024 - iWidth)
{
bHoriDir = 0;
}
}
else
{
iXPos = iXPos - iSpeed;
if (iXPos < 0)
{
bHoriDir = 1;
}
}
if (bVertDir == 1) //down
{
iYPos = iYPos + iSpeed;
if (iYPos > 768 - 64)
{
bVertDir = 0;
}
}
else
{
iYPos = iYPos - iSpeed;
if (iYPos < 0)
{
bVertDir = 1;
}
}
agk::SetSpritePosition(iSprID, iXPos, iYPos);
//END Move Ball
//Bat2Ball Collisions
/*if (agk::GetSpriteCollision(iSprID, BatID))
{
//We have collided.
//As Ball is bigger than the gap below the bat must have hit top or sides
//so just flip the vertcal direction
if (bVertDir == 1)
{
bVertDir = 0;
}
}*/
}
}
void BrickBall::Collided(int OtherSpriteToCheck)
{
if (agk::GetSpriteCollision(iSprID, OtherSpriteToCheck))
{
if (bVertDir == 1)
{
bVertDir = 0;
}
}
}
I don't know the cause of your error, but I got this exact error because I had an iterator to a vector, then I updated the vector, then I tried to dereference the iterator (e.g. access *myIterator). I'm new to C++, but it seems that if you alter a collection after getting an iterator for it, your iterator is no longer valid. You need to repoint your iterator to wherever it was.
All my sprites are reversed when I try to draw my isometric map.
Here is the tileset.png mentionned in the following code :
Object.h/Object.cpp
I can use them to draw tiles, UI element, etc. ...
#pragma once
class Object {
public:
//FUNCTIONS
Object();
void addComponent(float value);
int getComponent(float index);
void editComponent(float index, float value);
void deleteComponent(float index);
private:
vector<int> components;
};
#include "Object.cpp"
-
#pragma once
//FUNCTIONS
Object::Object() {
//...
}
void Object::addComponent(float value) {
components.push_back(value);
}
int Object::getComponent(float index) {
return components[index];
}
void Object::editComponent(float index, float value) {
components[index] = value;
}
void Object::deleteComponent(float index) {
components.erase(components.begin() + index);
}
Note: I may have weird includes, I'm struggling with visual studio ha ha.
Scene.h/Scene.cpp
Handle data & graphics
#pragma once
class Scene {
public:
Scene(float w, float h, int mapx, int mapy, int tilesize, int mapwidth, int mapheight);
void run();
void addLayer();
void loadTileset(sf::String url);
void loadUiTileset(sf::String url);
//functions
//...
//getters
//...
//setters
//...
private:
sf::RenderWindow window;
float width;
float height;
int nb_layers;
int map_x;
int map_y;
int map_width;
int map_height;
int tile_size;
int selected_tile_index;
sf::RenderTexture texture;
sf::Sprite tile;
sf::Sprite map;
sf::Texture tileset;
vector<Object> tiles;
sf::Texture uiTileset;
//private functions
void updateMap();
//...
void processEvent();
void update(sf::Time deltaTime);
void render();
//...
};
#include "Scene.cpp"
-
#pragma once
//functions
Scene::Scene(float w, float h, int mapx, int mapy, int tilesize, int mapwidth, int mapheight) : window(sf::VideoMode(w, h), "Editor") {
width = w;
height = h;
map_x = mapx;
map_y = mapy;
map_width = mapwidth;
map_height = mapheight;
tile_size = tilesize;
selected_tile_index = 0;//default
nb_layers = 0;
}
void Scene::run() {
sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
while (window.isOpen()) {
processEvent();
timeSinceLastUpdate += clock.restart();
while (timeSinceLastUpdate > TimePerFrame) {
timeSinceLastUpdate -= TimePerFrame;
processEvent();
update(TimePerFrame);
}
render();
}
}
void Scene::addLayer() {
nb_layers += 1;
int tile_x = map_x,
tile_y = map_y,
num_layer = nb_layers - 1,
layer_pos = (num_layer * tile_size) / 2,
tile_zOrder = -1;
tile_y -= layer_pos;
int x = map_x,
y = map_y;
for (int h = 0; h < map_height; h++) {
for (int w = 0; w < map_width; w++) {
tile_zOrder = (w * (h + 1)) + (num_layer * 10);
x = carthesianToIsometric(tile_x, tile_y)[0];
y = carthesianToIsometric(tile_x, tile_y)[1] - layer_pos;
cout << x << ", " << y << endl;
Object tile;
tile.addComponent(selected_tile_index);
tile.addComponent(x);
tile.addComponent(y);
tile.addComponent(tile_zOrder);
tile.addComponent(num_layer);
tiles.push_back(tile);
tile_x += tile_size;
}
tile_x = 0;
tile_y += tile_size;
}
updateMap();
}
void Scene::loadTileset(sf::String url) {
if (!tileset.loadFromFile(url))
{
cout << std::string(url) << "couldn't be loaded..." << endl;
}
}
void Scene::loadUiTileset(sf::String url) {
if (!uiTileset.loadFromFile(url))
{
cout << std::string(url) << "couldn't be loaded..." << endl;
}
}
//getters
//...
//setters
//...
//private functions
void Scene::updateMap() {
int tile_position_x = 0,
tile_position_y = 0;
int tile_x = 0,
tile_y = 0;
if (!texture.create(map_width * tile_size, (map_height * tile_size) / 2))
cout << "Texture couldn't be loaded... " << endl;
texture.clear(sf::Color(133, 118, 104, 255));
sf::Sprite image;
image.setTexture(tileset);
int tileset_width = image.getGlobalBounds().width,
tileset_height = image.getGlobalBounds().height;
tile.setTexture(tileset);
for (int tile_index = 0; tile_index < tiles.size(); tile_index++) {
tile_position_x = getTilePosition(tileset_width, tileset_height, tiles[tile_index].getComponent(0), tile_size)[0];
tile_position_y = getTilePosition(tileset_width, tileset_height, tiles[tile_index].getComponent(0), tile_size)[1];
tile.setTextureRect(sf::IntRect(tile_position_x, tile_position_y, tile_size, tile_size));
tile_x = tiles[tile_index].getComponent(1);
tile_y = tiles[tile_index].getComponent(2);
tile.setPosition(sf::Vector2f(tile_x, tile_y));
texture.draw(tile);
}
map.setTexture(texture.getTexture());
}
void Scene::processEvent() {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape)
window.close();
break;
}
}
}
void Scene::update(sf::Time deltaTime) {
//REMEMBER: distance = speed * time
//MOVEMENT, ANIMATIONS ETC. ..
}
void Scene::render() {
window.clear();
window.draw(map);
window.display();
}
main.cpp
#pragma once
//global functions + main headers + class headers =>
#include "globalfunctions.h"
int main() {
int map_width = 15,
map_height = 15,
tile_size = 64;
float scene_width = map_width * tile_size,
scene_height = (map_height * tile_size) / 2;
Scene engine(scene_width, scene_height, 0, 0, tile_size, map_width, map_height);
engine.loadTileset("tileset.png");
//engine.loadUiTileset("menu.png");
engine.addLayer();
//...
engine.run();
return EXIT_SUCCESS;
}
globalfunctions.h
Some utility functions.
getTilePosition(...) allow me to get x, y on a texture with a given tile index. Example : if I want to draw the tile n°0 of the tileset texture.
#pragma once
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
#include <math.h>
#include <vector>
using namespace std;
vector<float> getTilePosition(float tileset_width, float tileset_height, float tile_index, float tile_size) {//In a tileset
float tileX = 0,
tileY = 0,
tilePerLine = 0;
tilePerLine = tileset_width / tile_size;
tileY = floor(tile_index / tilePerLine);
tileX = ((tile_index + 1) - (tileY * tilePerLine)) - 1;
tileX *= tile_size;
tileY *= tile_size;
vector<float> coords;
coords.push_back(tileX);
coords.push_back(tileY);
return coords;
}
vector<int> carthesianToIsometric(int x, int y) {
vector<int> coords;
float isoX = (x - y) / 2,
isoY = (x + y) / 4;
coords.push_back(isoX);
coords.push_back(isoY);
return coords;
}
#include "Object.h"
#include "Scene.h"
//...
And here, the WTF result I get :
Thanks for reading all that weird code !
Edit :
When I change
tile.setPosition(sf::Vector2f(tile_x, tile_y));
to
tile.setPosition(sf::Vector2f(0, 0));
in updateMap() from scene.cpp :
Unfortunatly, I cannot explain why. Maybe it will help you to understand the problem.
In case someone encounter the same problem :
As #Spectre suggested it was a problem of the sfml function draw().
http://en.sfml-dev.org/forums/index.php?topic=6903.0
You need to use display on the sf::renderTexture after your cleared it.
I have been working in a zombie arcade game, I am experimenting an strange problem. I created a class called GameObject and a class called Human that inherits from GameOBject, then I declared two more called; Zombie and Player. The problem is that when I create two or more characters (instances) of this four classes the game stops working, I detected that in the classes Zombie and Player the problem only occurs when I call the method update (only these classes use it). This method is supposed to control the activities of each character.
The graphics and animated sprites are managed separately from the character, they (the characters) only call some methods to control the sprite update, those orders are processed when updating the screen.
I just want you to tell me what is happening, I think the problem is in the update method of the classes Zombie and Player.
The project is divided in different files.
I don't think this matters but I am using CodeBlocks and tdm-gcc.
main.cpp:
#include "game_classes.h"
bool working = true, redraw = true;
int main(){
init_game_classes();
Player player(200, 200);
std::vector<Zombie *> zombie_list;
for(unsigned int i = 0; i < 2; i++){
Zombie *zombie = new Zombie(i * 100, 12, &player);
zombie_list.push_back(zombie);
}
while(working){
input();
if(event.type == ALLEGRO_EVENT_TIMER){//main loop
player.update();
for(unsigned int i = 0; i < zombie_list.size(); i++){
zombie_list[i]->update();
}
redraw = true;
}
else if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE){
working = false;
}
if(redraw and al_is_event_queue_empty(event_queue)){//updating display
update_animated_models();
blit_models();
update_display();
redraw = false;
}
}
for(unsigned int i = 0; i < zombie_list.size(); i++)
delete zombie_list[i];
quit();
return 0;
}
render.h:
#include <vector>
#include <stdio.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
int global_ticks_per_frame = 0;
ALLEGRO_BITMAP *map_background = NULL;
class Model;
std::vector<Model *> model_list;
class Model{
protected:
int x = 0, y = 0, width, height;
int source_x = 0, source_y = 0, source_width, source_height;
public:
ALLEGRO_BITMAP *sheet = NULL;
bool hide = false;
int flags = 0;
Model(const char *path, int width, int height){
this->sheet = al_load_bitmap(path);
al_convert_mask_to_alpha(this->sheet, al_map_rgb(255, 0, 255));
this->source_width = al_get_bitmap_width(this->sheet);
this->source_height = al_get_bitmap_height(this->sheet);
this->width = width;
this->height = height;
model_list.insert(model_list.begin(), this);
}
Model(ALLEGRO_BITMAP *image, int width, int height){
this->sheet = image;
al_convert_mask_to_alpha(this->sheet, al_map_rgb(255, 0, 255));
this->source_width = al_get_bitmap_width(this->sheet);
this->source_height = al_get_bitmap_height(this->sheet);
this->width = width;
this->height = height;
model_list.insert(model_list.begin(), this);
}
~Model(){
al_destroy_bitmap(this->sheet);
model_list.erase(model_list.begin() + this->index());
}
void show(){
if(not this->hide){
al_draw_scaled_bitmap(this->sheet, this->source_x, this->source_y, this->source_width, this->source_height, x, y, width, height, this->flags);
}
}
void set_x(int x){
this->x = x;
}
unsigned int index(){
for(unsigned int i = 0; i < model_list.size(); i++){
if(model_list[i] == this)
return i;
}
}
bool set_y(int y){
this->y = y;
model_list.erase(model_list.begin() + this->index());
int this_relative_y = this->y + this->height;
unsigned int i = 0;
while(i < model_list.size()){
int from_list_relative_y = model_list[i]->y + model_list[i]->height;
if(this_relative_y < from_list_relative_y){
model_list.insert(model_list.begin() + i, this);
return false;
}
i += 1;
}
model_list.push_back(this);
return false;
}
int get_y(){
return this->y;
}
int get_x(){
return this->x;
}
unsigned int get_width(){
return this->width;
}
unsigned int get_height(){
return this->height;
}
};
void blit_models(){
for(unsigned int i = 0; i < model_list.size(); i++)
model_list[i]->show();
}
class AnimatedModel;
std::vector<AnimatedModel *> animated_model_list;
class AnimatedModel : public Model{
private:
unsigned int ticks_per_frame, ticks_counter = 0;
unsigned int current_frame = 0, frame_count;
public:
bool stop = false;
void set_speed(unsigned int new_speed){
this->ticks_per_frame = new_speed;
}
AnimatedModel(const char *path, unsigned int frames, unsigned int ticks_per_frame, int width, int height) : Model(path, width, height){
this->ticks_per_frame = ticks_per_frame;
this->frame_count = frames;
this->source_width /= frames;
animated_model_list.push_back(this);
}
AnimatedModel(ALLEGRO_BITMAP *image, unsigned int frames, unsigned int ticks_per_frame, int width, int height) : Model(image, width, height){
this->ticks_per_frame = ticks_per_frame;
this->frame_count = frames;
this->source_width /= frames;
animated_model_list.push_back(this);
}
~AnimatedModel(){
for(unsigned int i = 0; i < animated_model_list.size(); i++){
if(animated_model_list[i] == this){
animated_model_list.erase(animated_model_list.begin() + i);
}
}
}
void fix_sheet_looking(){
if(not this->stop)
this->source_x = this->current_frame*this->source_width;
}
void play(){
if(this->ticks_counter >= this->ticks_per_frame + global_ticks_per_frame){
this->current_frame += 1;
if(this->current_frame >= this->frame_count)
this->current_frame = 0;
this->ticks_counter = 0;
}
else{
this->ticks_counter += 1;
}
}
void update(){
if(not this->stop){
this->play();
this->fix_sheet_looking();
}
}
void set_frame(unsigned int i){
this->current_frame = i;
}
unsigned int get_frame(){
return this->current_frame;
}
};
void update_animated_models(){
for(unsigned int i = 0; i < animated_model_list.size(); i++)
animated_model_list[i]->update();
}
game_classes.h:
#include "render.h"
#include "touch.h"
#include "window_control.h"
#include <string>
#include <iostream>
ALLEGRO_BITMAP *zombie_sprite = NULL;
ALLEGRO_BITMAP *human_sprite = NULL;
void init_game_classes(){
init_window_control();
zombie_sprite = al_load_bitmap("textures/zombie/sprite.png");
human_sprite = al_load_bitmap("textures/human/sprite.png");
}
int control_key_up = ALLEGRO_KEY_UP, control_key_down = ALLEGRO_KEY_DOWN, control_key_right = ALLEGRO_KEY_RIGHT, control_key_left = ALLEGRO_KEY_LEFT;
int control_key_run = ALLEGRO_KEY_Z;
HitBoxList character_body_reg;
HitBoxList item_body_reg;
class GameObjec{
protected:
unsigned int walking_speed;
HitBoxList *last_coll_test = NULL;
int last_x, last_y;
int body_high;
int left_distance;
public:
HitBox *body = NULL;
AnimatedModel *sprite = NULL;
void set_x(int x){
this->sprite->set_x(x);
this->last_x = this->body->x;
this->body->x = x + this->left_distance;
}
void set_y(int y){
this->sprite->set_y(y);
this->last_y = this->body->y;
this->body->y = y + this->body_high;
}
int get_x(){
return this->sprite->get_x();
}
int get_y(){
return this->sprite->get_y();
}
void slide_x(short int direction){
character_body_reg.pop(this->body);
this->last_coll_test = this->body->slide_x(this->walking_speed*direction, &character_body_reg);
character_body_reg.push(this->body);
this->set_x(this->body->x - this->left_distance);
}
void slide_y(short int direction){
character_body_reg.pop(this->body);
this->last_coll_test = this->body->slide_y(this->walking_speed*direction, &character_body_reg);
character_body_reg.push(this->body);
this->set_y(this->body->y - this->body_high);
}
void show_hitbox(){
al_draw_rectangle(this->body->x, this->body->y, this->body->x + this->body->width, this->body->y + this->body->height, al_map_rgb(255, 0, 0), 1);
}
GameObjec(int x, int y, unsigned int walking_speed, const char *sprite_image_path, int frames, int ticks_per_frame, int sprite_width, int sprite_height, int body_high, int body_len){
this->walking_speed = walking_speed;
this->body_high = body_high;
this->sprite = new AnimatedModel(sprite_image_path, frames, ticks_per_frame, sprite_width, sprite_height);
this->left_distance = (this->sprite->get_width() - body_len)/2;
this->body = new HitBox;
this->sprite->set_x(x);
this->sprite->set_y(y);
this->body->width = body_len;
this->body->height = this->sprite->get_height() - this->body_high;
this->body->x = x + left_distance;
this->body->y = y + this->body_high;
this->last_x = this->body->x;
this->last_y = this->body->y;
character_body_reg.push(this->body);
}
~GameObjec(){
delete this->sprite;
character_body_reg.pop(this->body);
delete this->body;
}
void draw_hitbox(){
al_draw_rectangle(this->body->x, this->body->y, this->body->x + this->body->width, this->body->y + this->body->height, al_map_rgb(255, 0, 0), 0);
}
};
class Human : public GameObjec{
protected:
bool walking = false;
public:
Human(int x, int y, const char *sprite_image_path) : GameObjec(x, y, 2, sprite_image_path, 2, 7, 64, 80, 60, 32){}
~Human(){}
void walk_down(){
this->slide_y(1);
this->sprite->stop = false;
this->walking = true;
}
void walk_up(){
this->slide_y(-1);
this->sprite->stop = false;
this->walking = true;
}
void walk_right(){
this->slide_x(1);
this->sprite->stop = false;
this->sprite->flags = 0;
this->walking = true;
}
void walk_left(){
this->slide_x(-1);
this->sprite->stop = false;
this->sprite->flags = ALLEGRO_FLIP_HORIZONTAL;
this->walking = true;
}
};
class Player : public Human{
public:
Player(int x, int y) : Human(x, y, "textures/human/sprite.png"){
}
void control(){
if(get_key(control_key_down))
this->walk_down();
else if(get_key(control_key_up))
this->walk_up();
if(get_key(control_key_right))
this->walk_right();
else if(get_key(control_key_left))
this->walk_left();
if(not this->walking){
this->sprite->set_frame(0);
this->sprite->fix_sheet_looking();
this->sprite->stop = true;
}
if(this->last_x == this->body->x and this->last_y == this->body->y)
this->walking = false;
}
void update(){
this->control();
}
};
class Zombie : public Human{
private:
//Player *to_kill;
int to_kill_x, to_kill_y;
unsigned int walk_ticks_counter = 0, follow_ticks_counter = 0;
public:
Player *to_kill;
void fix_to_kill_position(){
if(this->to_kill){
this->to_kill_x = this->to_kill->body->x;
this->to_kill_y = this->to_kill->body->y;
}
else{
this->to_kill_x = this->body->x;
this->to_kill_y = this->body->y;
}
}
Zombie(int x, int y, Player *to_kill) : Human(x, y, "textures/zombie/sprite.png"){
this->sprite->set_speed(23);
this->walking_speed = 1;
this->to_kill = to_kill;
this->fix_to_kill_position();
}
void control(){
if(this->body->y < to_kill_y)
this->walk_down();
else if(this->body->y > to_kill_y)
this->walk_up();
if(this->body->x < to_kill_x)
this->walk_right();
else if(this->body->x > to_kill_x)
this->walk_left();
if(not this->walking){
this->sprite->set_frame(0);
this->sprite->fix_sheet_looking();
this->sprite->stop = true;
}
}
void update(){
if(this->follow_ticks_counter == 78){
this->fix_to_kill_position();
this->follow_ticks_counter = 0;
}
else{
this->follow_ticks_counter += 1;
}
if(this->walk_ticks_counter == 2){
this->control();
this->walk_ticks_counter = 0;
}
else{
this->walk_ticks_counter += 1;
}
if(this->last_x == this->body->x and this->last_y == this->body->y)
this->walking = false;
}
};