I am trying to create a variation of the classic Snake game.
Basically what i am stuck on is how to restrict the snake movement to a 2D array whereby there will be a 20x20 grid.
At the moment, my snake head which is just a shape drawn with a midpoint, moves freely one pixel at a time within the game board. I require the snake to move one grid space at a time.
This is my snake code:
void Snake::move()
{
switch(direction_){
case Direction::North:
position_.y += 1;
break;
case Direction::East:
position_.x += 1;
break;
case Direction::South:
position_.y -= 1;
break;
case Direction::West:
position_.x -= 1;
}
if (position_.x < 6.4) position_.x = 44.8; else if (position_.x > 44.8) position_.x = 6.4;
if (position_.y < 0) position_.y = 38.4; else if (position_.y > 38.4) position_.y = 0;
}
void Snake::render(prg::Canvas& canvas) const
{
canvas.drawCircle(getPosition().x * 20, getPosition().y * 20,19.2,prg::Colour::GREEN);
}
void Snake::changeDirection(Direction new_direction)
{
direction_ = new_direction;
}
This is the code that handles keyboard input/movement
PlayerSnake::PlayerSnake()
{
prg::application.addKeyListener(*this);
}
PlayerSnake::~PlayerSnake()
{
prg::application.removeKeyListener(*this);
}
bool PlayerSnake::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
if(key_event.key_state == KeyEvent::KB_DOWN) {
switch(key_event.key) {
case KeyEvent::KB_LEFT_KEY:
changeDirection(Direction::West);
break;
case KeyEvent::KB_RIGHT_KEY:
changeDirection(Direction::East);
break;
case KeyEvent::KB_UP_KEY:
changeDirection(Direction::North);
break;
case KeyEvent::KB_DOWN_KEY:
changeDirection(Direction::South);
break;
}
}
return true;
}
I'm in desperate need of any suggestions and have been racking my brain trying to get the snake to move along a grid. I'm also new-ish to C++ so please understand.
Thanks :)
Don't think of the screen as the data. The screen is a representation of the data. This means that you have to figure out a way to map the data to it's visual equivalent.
If the grid is 20x20, but the screen rendering is 200x200, this implies a 1:10 ratio of pixels to cells. So, one possible method of drawing might look like this. (Sorry, using Processing syntax.)
In processing, one method of drawing a rectangle is using the command rect(int left, int top, int right, int bottom);
So, one implementation might look like this:
void draw_square(int cellx, int celly)
{
rect(cellx*10, celly*10, cellx*10+10, celly*10+10);
}
Related
Trying to make a collision system in sfml for the first time in SFML without using a tutorial, using a array-based thing like so:
bool moright, moleft, moup, xcollision, ycollision;
float xvel, yvel;
int position, predictx, predicty, cm, arraynum;
class playerClass{
public:
playerClass(){
}
void update()
{
if (moright == true){
xvel = 2;}
if (moleft == true){
xvel = -2;}
if (!(moright || moleft)){
if (xvel < 0)
xvel = 0;
if (xvel > 0)
xvel = 0;}
}
};
int main()
{
playerClass playerObject;
// Create the main window
RenderWindow window(VideoMode(1080, 720), "SFML window");
// Load a sprite to display
Texture texture;
if (!texture.loadFromFile("gsquare100x100.png"))
return EXIT_FAILURE;
Sprite sprite(texture);
Sprite floor(texture);
Sprite wall(texture);
floor.setPosition(Vector2f(0.f, 498.f));
wall.setPosition(Vector2f(598.f,0.f));
floor.setColor(Color(0, 255, 0));
floor.setScale(12.f, 12.f);
wall.setScale(12.f, 12.f);
wall.setColor(Color(0, 0, 255));
int collisions[2][4]{
{0, 400, 500, 600},
};
// Start the game loop
while (window.isOpen())
{
Vector2f position = sprite.getPosition();
cout << position.y << endl;
predictx = position.x + xvel;
predicty = position.y + yvel;
yvel = 1;
for (arraynum = 0; arraynum < 2; arraynum++){
if ((predictx > collisions[arraynum][0])&&(predictx < collisions[arraynum][1])&&(predicty > collisions[arraynum][2])&&(predicty < collisions[arraynum][3])){
if ((position.y > collisions[arraynum][3])||(position.y < collisions[arraynum][2])){
xcollision = true;}
if ((position.x > collisions[arraynum][1])||(position.x < collisions[arraynum][0])){
ycollision = true;}
}
}
if (xcollision == true)
xvel = 0;
xcollision = false;
if (ycollision == true)
yvel = 0;
ycollision = false;
sprite.move(sf::Vector2f(0.f+xvel, 0.f+yvel));
// Process events
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == Event::KeyPressed)
{if (event.key.code == Keyboard::D)
moright = true;
if (event.key.code == Keyboard::A)
moleft = true;}
if (event.type == Event::KeyReleased)
{if (event.key.code == Keyboard::D)
moright = false;
if (event.key.code == Keyboard::A)
moleft = false;}
playerObject.update();
}
However the collision never registers, removing the bit that checks from which direction the sprite is moving in from doesn't help.
Very new to c++ so apologies if this is a stupid question and for my likely overly elaborate collision system.
I've written simple collisions with SFML before, and here's my advice to you: make your code as readable as possible! Things are going to get more complicated, and you need to have a system is reusable and easy to understand.
I've read your code but I don't understand why you've used an array. I assume you're trying to check if a smaller rectangle sprite is about to exit the collisions array?
For this purpose I suggest using a FloatRect object. It has useful functions like .contains() and .intersects() that you might need in the future. One downside it that is has top and left only, and to make it more and short, we'll define a simple struct to handle that part for us, as well as work for rectangular sprites as well.
I've left comments that explain the code, but haven't tested it personally. You can do that and integrate what you've learned into your project. Good luck
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
using namespace sf;
//using a struct is not necessarily faster. BUT it does give your code more readability and is reusable for future needs
//this struct just stores a floatRect of the given sprite/Floatrecct, defining some useful functions allowing for shorter code and more readability
struct rectangularShape
{
FloatRect containingRectangle;
//constructor with sprite input
rectangularShape(Sprite sprite)
{
this -> containingRectangle = FloatRect(Vector2f(sprite.getGlobalBounds().left, sprite.getGlobalBounds().top),
Vector2f(sprite.getGlobalBounds().left + sprite.getGlobalBounds().width,sprite.getGlobalBounds().top + sprite.getGlobalBounds().height));
}
//constructor with floatrect
rectangularShape(FloatRect rect)
{
this -> containingRectangle = rect;
}
//any useful functions for rectangular shapes-- you could add more if you want
float getTop() {return containingRectangle.top;}
float getbottom() {return containingRectangle.top + containingRectangle.height;}
float getleft() {return containingRectangle.left;}
float getright() {return containingRectangle.left + containingRectangle.width;}
};
//note the use of a FloatRect instead of the arrays you were using, this just makes it easier to understand
FloatRect inclusionArea(TopLeftVector, BottomRightVector);
Sprite sprite(texture);
//declare rectangularShapes, here we check if smallRectangle is exiting it's container
rectangularShape containingRectangle(inclusionArea);
rectangularShape smallRectangle(sprite);
//alternatively you can use the sprite's next position:
/*
spriteCopy = sprite;
spriteCopy.move(deltaTime * Vector2f(xSpeed, ySpeed));
rectangularShape smallRectangle(spriteCopy);
*/
//do the check:
if (smallRectangle.getTop() < containingRectangle.getTop() or smallRectangle.getBottom() > containingRectangle.getBottom())
//exiting in Y axis
//do something;
;
if (smallRectangle.getLeft() < containingRectangle.getLeft() or smallRectangle.getRight() > containingRectangle.getRight())
//exiting in X axis
//do something;
;
I can't comment due to low reputation.
From the code presented, it's seems like you never set xcollision or ycollision to true anywhere.
I want to display a customized item, basically a colored table with variable column number and width. I'm using c++ Builder XE2 Rad Studio for this.
So, I created a new class inherting from TGraphicControl overwriting void __fastcall Paint(void).
The Problem: it takes quite some time (between 15 and 30 ms) to draw 12 colored rectangles with text, so I expect to have done something wrong. I suspect ShadowRect to do things one could implement better, but I'm not really sure...
Does anyone see my mistake here?
Code to Draw the Paint() Event:
void __fastcall CustomTrgDrawings::Paint(void){
ResetCanvasTools();
ShadowRect(ClientRect,colBG-0x111111,coladdLight,colText,"");
Canvas->TextOutA(5,8,String().sprintf(L"Logic Box %u",fid));
Canvas->Font->Style = TFontStyles() << fsBold;
ShadowRect(nameRec,colBtnBG,coladdLight,colText,fName);
Canvas->Font->Style = TFontStyles() ;
ShadowRect(ch1Rec,colBtnBG-0x110011,coladdLight,colText,channels->Strings[fch1Id],true,3,true);
ShadowRect(ch2Rec,colBtnBG-0x110011,coladdLight,colText,channels->Strings[fch2Id],true,3,true);
ShadowRect(ch3Rec,colBtnBG-0x110011,coladdLight,colText,channels->Strings[fch3Id],true,3,true);
ShadowRect(ch4Rec,colBtnBG-0x110011,coladdLight,colText,channels->Strings[fch4Id],true,3,true);
ShadowRect(norm1Rec,colBtnBG-0x000011,coladdLight,colText,norms->Strings[fnorm1],true,3,true);
ShadowRect(norm2Rec,colBtnBG-0x000011,coladdLight,colText,norms->Strings[fnorm2],true,3,true);
ShadowRect(norm3Rec,colBtnBG-0x000011,coladdLight,colText,norms->Strings[fnorm3],true,3,true);
ShadowRect(norm4Rec,colBtnBG-0x000011,coladdLight,colText,norms->Strings[fnorm4],true,3,true);
ShadowRect(logicRec,colBtnBG-0x002222,coladdLight,colText,logics->Strings[flogicId],true,3,true);
ShadowRect(normOutRec,colBtnBG-0x002200,coladdLight,colText,norms->Strings[fnormOut],true,3,true);
}
void CustomTrgDrawings::ResetCanvasTools(){
Canvas->Brush->Color=clNone;
Canvas->Brush->Style=bsClear;
Canvas->Pen->Color=clNone;
Canvas->Pen->Mode=pmCopy;
Canvas->Pen->Style=psSolid;
Canvas->Pen->Width=1;
Canvas->Font->Color=clBlack;
Canvas->Font->Size=8;
Canvas->Font->Style=TFontStyles();
}
void CustomTrgDrawings::ShadowRect(const TRect pos,const TColor bg, const TColor add,const TColor fg, const String text,const bool shadow,const int align,const bool comboIcon){
int textLen;
int textX,textY;
int iconWidth=0;
Canvas->Brush->Style=bsSolid;
Canvas->Brush->Color=bg;
Canvas->Pen->Color=bg-4*add;
Canvas->Pen->Style=psSolid;
Canvas->Pen->Width=1;
Canvas->FillRect(pos);
if(shadow){
Canvas->Pen->Color=bg-2*add;
Canvas->MoveTo(pos.Left,pos.Bottom-1);
Canvas->LineTo(pos.Right-1,pos.Bottom-1);
Canvas->LineTo(pos.Right-1,pos.Top);
Canvas->Pen->Color=bg+2*add;
Canvas->MoveTo(pos.Right-1,pos.Top);
Canvas->LineTo(pos.Left,pos.Top);
Canvas->LineTo(pos.Left,pos.Bottom-1);
}
if(comboIcon){
iconWidth=6;
Canvas->Pen->Style=psSolid;
Canvas->Pen->Mode=pmMask;
Canvas->Pen->Width=3;
Canvas->Pen->Color=bg-2*add;
Canvas->MoveTo(pos.Right-iconWidth-5,pos.Top+6);
Canvas->LineTo(pos.Right-iconWidth/2-5,pos.Top+10);
Canvas->LineTo(pos.Right-5,pos.Top+6);
}
Canvas->Brush->Style=bsClear;
Canvas->Pen->Color=fg;
textLen=Canvas->TextWidth(text);
switch(align%3){ //horizontal position
case 0: //left
textX=3;
break;
case 1: //middle
textX=((pos.Width()-iconWidth)-textLen)/2;
break;
case 2: //right
textX=(pos.Width()-iconWidth)-textLen;
break;
}
switch(align/3){ //vertical position
case 0: //top
textY=-1;
break;
case 1: //middle
textY=(pos.Height()-Canvas->TextHeight(text))/2;
break;
case 2: //bottom
textY=pos.Height()-Canvas->TextHeight(text);
break;
}
Canvas->TextOutA(pos.Left+textX,pos.Top+textY,text);
}
Hi i'm currently making an RPG similar to Legend of Zelda. I have a feature in my game where when Player attacks enemy with his sword, the enemy is knocked back n units. i have a collision detection that sometimes works as intended, and other times the enemy goes through the wall and gets stuck on the other side, and then other times the enemy can simply walk right through the wall. If possible, making the enemy move toward player upon collision with wall would be one possible solution to this problem I believe, but I do not know how to implement this. Here is my current collision code:
// Enemy Collides with Wall
counter1 = 0;
for (iter4 = enemyArray.begin(); iter4 != enemyArray.end(); iter4++)
{
counter2 = 0;
for (iter15 = wallArray.begin(); iter15 != wallArray.end(); iter15++)
{
if (enemyArray[counter1].rect.getGlobalBounds().intersects(wallArray[counter2].rect.getGlobalBounds()))
{
enemyArray[counter1].isCollided = true;
//Hit Wall
if ((enemyArray[counter1].direction == 1 || enemyArray[counter1].rect.getPosition().y >= wallArray[counter2].rect.getPosition().y)) //up
{
enemyArray[counter1].canMoveUp = false;
enemyArray[counter1].canMoveLeft = false;
enemyArray[counter1].canMoveRight = false;
enemyArray[counter1].rect.move(0, 7);
}
else if ((enemyArray[counter1].direction == 2 || enemyArray[counter1].rect.getPosition().y <= wallArray[counter2].rect.getPosition().y)) //Down
{
enemyArray[counter1].canMoveDown = false;
enemyArray[counter1].canMoveRight = false;
enemyArray[counter1].canMoveLeft = false;
enemyArray[counter1].rect.move(0, -7);
}
else if ((enemyArray[counter1].direction == 3 || enemyArray[counter1].rect.getPosition().x >= wallArray[counter2].rect.getPosition().x)) //Left
{
enemyArray[counter1].canMoveLeft = false;
enemyArray[counter1].canMoveUp = false;
enemyArray[counter1].canMoveDown = false;
enemyArray[counter1].rect.move(7, 0);
}
else if ((enemyArray[counter1].direction == 4 || enemyArray[counter1].rect.getPosition().x <= wallArray[counter2].rect.getPosition().x)) //Right
{
enemyArray[counter1].canMoveRight = false;
enemyArray[counter1].canMoveUp = false;
enemyArray[counter1].canMoveDown = false;
enemyArray[counter1].rect.move(-7, 0);
}
}
counter2++;
}
counter1++;
}
//Knock Back enemy away from sword && sword2
counterKnockBack++;
counter2 = 0;
for (iter4 = enemyArray.begin(); iter4 != enemyArray.end(); iter4++)
{
if (enemyArray[counter2].knockback == true)
{
if (enemyArray[counter2].isCollided == false)
{
if ((Player1.rect.getPosition().y > enemyArray[counter2].rect.getPosition().y))
{
enemyArray[counter2].rect.move(0, -3); //up
}
else if ((Player1.rect.getPosition().y < enemyArray[counter2].rect.getPosition().y))
{
enemyArray[counter2].rect.move(0, 3); //down
}
if ((Player1.rect.getPosition().x > enemyArray[counter2].rect.getPosition().x))
{
enemyArray[counter2].rect.move(-3, 0); //left
}
else if ((Player1.rect.getPosition().x < enemyArray[counter2].rect.getPosition().x))
{
enemyArray[counter2].rect.move(3, 0); //right
}
if (counterKnockBack >= 20)
{
enemyArray[counter2].knockback = false;
}
}
}
counter2++;
}
//turn off collided counter
counterCollided++;
counter2 = 0;
for (iter4 = enemyArray.begin(); iter4 != enemyArray.end(); iter4++)
{
if (enemyArray[counter2].isCollided == true)
{
if (counterCollided >= 30)
enemyArray[counter2].isCollided = false;
}
counter2++;
}
I have no idea why the enemy is able to sometimes simply walk right through the wall without being knocked back. So how can I fix this, any ideas?
Without reading the code completely I can already tell you that your collision detection code is wrong. Mostly because you are moving your objects directly, probably without checking for collisions within your rect::move function.
It is possible that the rect::move function will move objects through the walls without triggering any collision reaction code. Consider the following scenario:
First frame:
enemyArray[counterX].rect.move(3, 0);
Second frame:
The enemy object is moved behind the wall and will not trigger the collision detection code.
My advise is (despite the obvious one: read some books): for each enemy store its previous location and check for collision not between the enemy and the wall's rectangle but between wall and a rectangle between two enemy positions. Something like that:
This is of course only one of the possible scenarios when your collision detection code would be error-prone.
i am making a pong game, lPad is left pad and rPad is the right pad, but i have a problem when any pad is moving up and when i release the up button and press down the pad stops for a while and then goes down, the other thing is i can't move both pads when pressing both controls(only one is moving) with this setup:
if(e.type == SDL_KEYDOWN) {
switch(e.key.keysym.sym) {
case SDLK_s:
lPad.y += 8;
if(lPad.y >= s.SCREEN_HEIGHT - lPad.getHeight()) {
lPad.y = s.SCREEN_HEIGHT - lPad.getHeight();
}
break;
case SDLK_w:
lPad.y -= 8;
if(lPad.y <= 0) {
lPad.y = 0;
}
break;
case SDLK_DOWN:
rPad.y += 8;
if(rPad.y >= s.SCREEN_HEIGHT - rPad.getHeight()) {
rPad.y = s.SCREEN_HEIGHT - rPad.getHeight();
}
break;
case SDLK_UP:
rPad.y -= 8;
if(rPad.y <= 0) {
rPad.y = 0;
}
break;
default:
break;
}
}
Any idea how to fix this and make it smooth ?
It's better to use SDL_GetKeyboardState(NULL) as the function to get input. This way, you can get the entire state of the keyboard simultaneously and thus allow for parallel inputs. If you use the while loop, each event will get caught individually, and thus be choppy.
Here is some sample code on how to use it:
const auto * keys = SDL_GetKeyboardState(NULL);
while(!done) {
while(SDL_PollEvent(&e)) {
if(e.type == SDL_QUIT) {
done = true;
}
}
SDL_PumpEvents();
if (keys[SDL_SCANCODE_UP]) {
p1.set_speed(0, -60000 * delta.count());
}
if (keys[SDL_SCANCODE_DOWN]) {
p1.set_speed(0, 60000 * delta.count());
}
if (keys[SDL_SCANCODE_LEFT]) {
p1.set_speed(-60000 * delta.count(), 0);
}
if (keys[SDL_SCANCODE_RIGHT]) {
p1.set_speed(60000 * delta.count(), 0);
}
Also, might I suggest having a speed variable? Using pixels is not a good way to scale movement, as it depends on the resolution of the screen. Using something based on a time step is much more robust.
I have a problem with my 2d platformer. As i have just started out with c++ i'm having trouble with tile collision. I'm able to prevent the player from entering the tile and also being able to move away from it but somehow he cant move along the tile.
This is the function for checking if the new position is inside a solid tile:
void Maps::drawColMap(Player& _player){
for (int i = 0; i < _player.tiles.size(); i++)
{
if (colMap[_player.tiles[i].y][_player.tiles[i].x] == 1) //solid tile = 1
{
_player.willCollide = true;
break;
}
else {
_player.willCollide = false;
}
}
}
And here is the code for moving the player:
void Player::update()
{
sf::Vector2f newPosition;
sf::Vector2f oldPosition;
oldPosition.x = playerImage.getPosition().x; // store the current position
oldPosition.y = playerImage.getPosition().y;
newPosition.x = playerImage.getPosition().x; // store the new position
newPosition.y = playerImage.getPosition().y;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
source.y = Left; //sprite stuff
moving = true;
newPosition.x -= 2;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
source.y = Right;
moving = true;
newPosition.x += 2;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
source.y = Up;
moving = true;
newPosition.y -= 2;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
source.y = Down;
moving = true;
newPosition.y += 2;
}
if (!(sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down)))
{
moving = false;
}
//create corners to check collision
bottom = newPosition.y + 32; //tile size is 32 px
left = newPosition.x;
right = newPosition.x + 32;
top = newPosition.y;
sf::Vector2i topLeft(sf::Vector2i((int)left / 32, (int)top / 32)); // get the corners of the new position
sf::Vector2i topRight(sf::Vector2i((int)right / 32, (int)top / 32));
sf::Vector2i bottomLeft(sf::Vector2i((int)left / 32, (int)bottom / 32));
sf::Vector2i bottomRight(sf::Vector2i((int)right / 32, (int)bottom / 32));
tiles.clear();
tiles.push_back(topLeft);
if (std::find(tiles.begin(), tiles.end(), topRight) == tiles.end()) tiles.push_back(topRight); //check the corners
if (std::find(tiles.begin(), tiles.end(), bottomLeft) == tiles.end()) tiles.push_back(bottomLeft);
if (std::find(tiles.begin(), tiles.end(), bottomRight) == tiles.end()) tiles.push_back(bottomRight);
//if no collision set the position to the new position
if (!willCollide)
playerImage.setPosition(newPosition);
else
playerImage.setPosition(oldPosition); //if collision then set the position to the previous position
}
Any help is appreciated!
//edit 1
I tried logging the collision and it says that the player is still in the collision area even if i dont press anything. But how do i prevent the player from entering? I cant find the problem.
//edit 2
I think i found another problem.
The collision check should probably be run just before moving the player and after moving the new position.
After the player collides, and you change the positions, move it over by 1 pixel in the away from the collision area (the appropriate direction). It's possible that your corner is still within the collision bounds if it wasn't moved out of of the collision area properly.