Xcode Collision Detection Error Cocos2D Assertion Error - cocos2d-iphone

I am making a ball game in cocos2D, where you can slide the ball in a direction, and it will travel until it collides with a wall. My collision detection is giving me an "assertion error" when I swipe the ball in a direction.
I am using tiledmap to make the levels.
The way I've coded it is that I find the tilesize that my ball is in, then, in a for loop, I check if there are any wall sprites around the ball.
Any ideas where its going wrong?
-(void)callEveryFrame:(ccTime)dt{
//Error Here vvv (Assertion Failure in -[CCTMXLayer tileAt:])
NSLog(#"First Touch x: /%f Last Touch x: /%f First Touch y: /%f Last Touch y: /%f", firstTouch.x, lastTouch.x, firstTouch.y, lastTouch.y);
if(swipeLeft || swipeRight || swipeDown || swipeUp)
{
bool collision = false;
int ballTileX = (player.position.x / level1.tileSize.width);
int ballTileY = (player.position.y / level1.tileSize.width);
int collisionAreaX = ballTileX-1;
int collisionAreaY = ballTileY-1;
for(int i = 0; i < 9; i++)
{
bool tilePresent = [background tileAt:CGPointMake(collisionAreaX, collisionAreaY)] != NULL;
if(tilePresent && CGRectIntersectsRect(CGRectMake(collisionAreaX*level1.tileSize.width, collisionAreaY*level1.tileSize.height, level1.tileSize.width, level1.tileSize.height), CGRectMake(ballTileX*level1.tileSize.width, ballTileY*level1.tileSize.height, level1.tileSize.width, level1.tileSize.height)))
{
collision = true;
break;
}
if(i % 3 == 0)
{
collisionAreaX -= 2;
collisionAreaY++;
}
else
{
collisionAreaX++;
}
}
if(collision) {
swipeLeft = false;
swipeRight = false;
swipeDown = false;
swipeUp = false;
}
}
}

Related

Water in a falling sand simulation

I am currently working on a very simple 'Falling Sand' simulation game in C++ and SDL2, and am having problems with getting water to flow in a more realistic manner. I basically have a grid of cells that I iterate through bottom-to-top, left-to-right and if I find a water cell, I just check below, down to left, down to the right, left then right for empty cells and it moves into the first one its finds (it makes a random choice if both diagonal cells or both horizontal cells are free). I then mark the cell it moved into as processed so that it is not checked again for the rest of that loop.
My problem is a sort of 'left-bias' in how the particles move; if I spawn a square of water cells above a barrier, they will basically all shift to left without moving once the particles begin to reach the barrier, while the cells on the right will run down in the proper way. So instead of forming a nice triangular shape flowing out evenly to both sides, the whole shape will just move to the left. This effect is reversed whenever I iterate left-to-right, so I know it's something to do with that but so far I've been stumped trying to fix it. I initially thought it was a problem with how I marked the cells as processed but I've found no obvious bugs with that system in many hours of testing. Has anyone faced any similar challeneges in developing a simulation like this, or knows something that I'm missing? Any help would be very much appreciated.
EDIT:
Ok so I've made a little progress, however I've ran into another bug that seems to be unrelated to iteration, since now I save a copy of the old cells and read from that to decide an update, then update the original cells and display that. This already made the sand work better, however water, which checks horizontally for free cells, now 'disappears' when it does move horizontally. I've been testing it all morning and have yet to find a solution, I thought it might've been someting to do with how I was copying the arrays over, but it seems to work as far as I can tell.
New snippets:
Simulation.cpp
void Simulation::update()
{
copyStates(m_cells, m_oldCells); // so now oldcells is the last new state
for(int y = m_height - 1; y>= 0; y--)
for(int x = 0; x < m_width; x++)
{
Cell* c = getOldCell(x, y); // check in the old state for possible updates
switch(c->m_type)
{
case EMPTY:
break;
case SAND:
if(c->m_visited == false) update_sand(x, y);
break;
case WATER:
if(c->m_visited == false) update_water(x, y);
break;
default:
break;
}
}
}
void Simulation::update_water(int x, int y)
{
bool down = (getOldCell(x, y+1)->m_type == EMPTY) && checkBounds(x, y+1) && !getOldCell(x, y+1)->m_visited;
bool d_left = (getOldCell(x-1, y+1)->m_type == EMPTY) && checkBounds(x-1, y+1) && !getOldCell(x-1, y+1)->m_visited;
bool d_right = (getOldCell(x+1, y+1)->m_type == EMPTY) && checkBounds(x+1, y+1) && !getOldCell(x+1, y+1)->m_visited ;
bool left = (getOldCell(x-1, y)->m_type == EMPTY) && checkBounds(x-1, y) && !getOldCell(x-1, y)->m_visited ;
bool right = (getOldCell(x+1, y)->m_type == EMPTY) && checkBounds(x+1, y) && !getOldCell(x+1, y)->m_visited ;
// choose random dir if both are possible
if(d_left && d_right)
{
int r = rand() % 2;
if(r) d_right = false;
else d_left = false;
}
if(left && right)
{
int r = rand() % 2;
if(r) right = false;
else left = false;
}
if(down)
{
getCell(x, y+1)->m_type = WATER; // we now update the new state
getOldCell(x, y+1)->m_visited = true; // mark as visited so it will not be checked again in update()
} else if(d_left)
{
getCell(x-1, y+1)->m_type = WATER;
getOldCell(x-1, y+1)->m_visited = true;
} else if(d_right)
{
getCell(x+1, y+1)->m_type = WATER;
getOldCell(x+1, y+1)->m_visited = true;
} else if(left)
{
getCell(x-1, y)->m_type = WATER;
getOldCell(x-1, y)->m_visited = true;
} else if(right)
{
getCell(x+1, y)->m_type = WATER;
getOldCell(x+1, y)->m_visited = true;
}
if(down || d_right || d_left || left || right) // the original cell is now empty; update the new state
{
getCell(x, y)->m_type = EMPTY;
}
}
void Simulation::copyStates(Cell* from, Cell* to)
{
for(int x = 0; x < m_width; x++)
for(int y = 0; y < m_height; y++)
{
to[x + y * m_width].m_type = from[x + y * m_width].m_type;
to[x + y * m_width].m_visited = from[x + y * m_width].m_visited;
}
}
Main.cpp
sim.update();
Uint32 c_sand = 0xedec9a00;
for(int y = 0; y < sim.m_height; y++)
for(int x = 0; x < sim.m_width; x++)
{
sim.getCell(x, y)->m_visited = false;
if(sim.getCell(x, y)->m_type == 0) screen.setPixel(x, y, 0);
if(sim.getCell(x, y)->m_type == 1) screen.setPixel(x, y, c_sand);
if(sim.getCell(x, y)->m_type == 2) screen.setPixel(x, y, 0x0000cc00);
}
screen.render();
I've attached a gif showing the problem, hopefully this might help make it a little clearer. You can see the sand being placed normally, then the water and the strange patterns it makes after being placed (notice how it moves off to the left when it's spawned, unlike the sand)
You also have to mark the destination postion as visited to stop multiple cells moving in to the same place.

Why setPosition work in some condition but doesn't work in other condition?

i make a game let's say ThrowBall, the Player can pick up the ball spawned and throw it into target get the score added then the ball return it's position and repeat. The problem is when Ball dragged by Player (i make the Ball as child of Player) into target, the Ball return it's position correctly into desired position, but the odd happens when it collided after i send the Ball into target by applying impulse it won't return the ball into desired position, why is this happening?
i'm running this game for android, i'm running this code in VS'17 using cocos2d-x-3.17. I tried changing impulse into force, moveby. I tried making the ball stop (setVelocity to zero before setPosition). I tried changing Point into Vect, vec2. I tried to break point debug, the code do read setPosition but do nothing.
GameScene.cpp
bool GameScene::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
auto pickupListener = EventListenerPhysicsContact::create();
pickupListener->onContactBegin = CC_CALLBACK_1(GameScene::onContactBegin, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(pickupListener, this);
return true;
}
bool GameScene::onContactBegin(cocos2d::PhysicsContact &contact)
{
PhysicsBody *a = contact.getShapeA()->getBody();
PhysicsBody *b = contact.getShapeB()->getBody();
if (
(BALL_COLLISION_BITMASK == a->getCollisionBitmask() && PLAYER_COLLISION_BITMASK == b->getCollisionBitmask()) ||
(BALL_COLLISION_BITMASK == b->getCollisionBitmask() && PLAYER_COLLISION_BITMASK == a->getCollisionBitmask())
)
{
// make the ball follow Player
isfollow = true;
}
else if (
(BALL_COLLISION_BITMASK == a->getCollisionBitmask() && TARGET_COLLISION_BITMASK == b->getCollisionBitmask()) ||
(BALL_COLLISION_BITMASK == b->getCollisionBitmask() && TARGET_COLLISION_BITMASK == a->getCollisionBitmask())
)
{
isfollow = false;
// add score
score++;
__String *tempScore = __String::createWithFormat("%i", score);
scoreLabel->setString(tempScore->getCString());
// return spawn ball
ball->returnPos();
return false;
}
void GameScene::throwBall() {
if (isfollow == true) {
isfollow = false;
ball->getSprite()->getPhysicsBody()->applyImpulse(Vec2(100000, 100000));
}
}
Ball.h
class Ball
{
public:
Ball(cocos2d::Layer *layer);
cocos2d::Sprite *getSprite() { return randomSpawn; };
void returnPos();
private:
cocos2d::Size visibleSize;
cocos2d::Vec2 origin;
cocos2d::Sprite *randomSpawn;
};
Ball.cpp
Ball::Ball(cocos2d::Layer *layer)
{
visibleSize = Director::getInstance()->getVisibleSize();
origin = Director::getInstance()->getVisibleOrigin();
randomSpawn = Sprite::create("res/ball.png");
randomSpawn->setPosition(Vec2(400, 80));
auto randomBallBody = PhysicsBody::createCircle(randomSpawn->getContentSize().width / 2);
randomBallBody->setCollisionBitmask(BALL_COLLISION_BITMASK);
randomBallBody->setContactTestBitmask(true);
randomBallBody->setGravityEnable(false);
randomSpawn->setPhysicsBody(randomBallBody);
layer->addChild(randomSpawn);
}
void Ball::returnPos()
{
randomSpawn->setPosition(Vec2(400, 80));
}
i want to make the object (Ball) return position when collided into Target and repeat. i'm sorry if the format is a mess, i'm new here, also it's not my full code, it's actually works fine i can run it, but only the setPosition won't work

Game Over condition simple Snake c++ game

I have basic knowledge of c++ and am trying to create a simple Snake game that runs in a while loop, as long as a "Game Over" condition evaluates to false.
If it turns to "true" (when the snakes head goes out of bounds), "Game Over!" is printed on a lcd screen.
For some reason, the code skips directly to the game over screen without running the game itself.
My code involves a few classes, and in one of those I have a collision detection function that looks like this:
bool SnakeEngine::collision_detection()
{
// check the snake coordinates so that the head doesn't go off screen
if (_head_y < 1) {
return 1;
}
if (_head_y > HEIGHT - 4) {
return 1;
}
if (_head_x < 1) {
return 1;
}
if (_head_x > WIDTH - 4) {
return 1;
} else {
return 0;
}
}
And in the main loop I have:
int main()
{
snake.draw(lcd); // draw the initial game frame and the sprites
lcd.refresh();
while(!snake.collision_detection()) {
snake.read_input(pad); // reads the user input
snake.update(pad); // updates the sprites positions and calls the collision_detection() function
render(); // clears the lcd, draws the sprites in the updated positions, refreshes the lcd
wait(1.0f/fps);
}
lcd.printString("Game Over!",6,4);
lcd.refresh();
}
How come is this not working?
Thank you
The collision detection is a suspect. If all specific conditions which you check do not return true (1) the final result should be false (0).
This condition is too restrictive:
if (_head_x > WIDTH - 4) {
return 1;
} else {
return 0;
}
It should be limited to:
if (_head_x > WIDTH - 4) {
return 1;
}
return 0;
The modified code with the use of bool types true and false looks like:
bool SnakeEngine::collision_detection()
{
// check the snake coordinates so that the head doesn't go off screen
if (_head_y < 1) {
return true;
}
if (_head_y > HEIGHT - 4) {
return true;
}
if (_head_x < 1) {
return true;
}
if (_head_x > WIDTH - 4) {
return true;
}
return false;
}
Try this, just guessing.I think when all the four conditions are false, than you should conclude that there is no collision. I think you did a blunder in the last else statement in your collision_detection() .
bool SnakeEngine::collision_detection()
{
// check the snake coordinates so that the head doesn't go off screen
if ( _head_y < 1 || _head_y > (HEIGHT - 4) || _head_x < 1 || _head_x > (WIDTH - 4) )
return true;
return false;
}

advanced Collision detection?

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.

Tilemap collision sfml c++ platformer

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.