I use cocos2d-x-3.13...
I'm just starting out but I've had problems with something: I have a number of sprites in a vector and every second I move them to each position, the problem arises when I want to delete them, since I have a function that moves them With a loop:
HelloWorldScene.cpp
bool HelloWorld::init() {
...
_enemies.reserve(15);
for (unsigned i = 0; i < 5; i++) {
//Here I create the sprites, and I activate the physics in each one :p
_enemies.push_back(sprite);
}
...
/*
This event checks for each collision, what I do is find the sprite
in my vector and then delete the sprite with "removeFromParent ()",
then delete the sprite from my vector. it's good, no? :v
*/
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = [=](PhysicsContact &contact) {
auto a = contact.getShapeA()->getBody(); //bullet
auto b = contact.getShapeB()->getBody(); //enemy
if (a->getCollisionBitmask() == 2 && b->getCollisionBitmask() == 2) {
a->getNode()->removeFromParent();
auto f = std::find(_enemies.begin(), _enemies.end(), ((cocos2d::Sprite*)b->getNode()));
if (f != _enemies.end()) {
_enemies.at(std::distance(_enemies.begin(), f))->removeFromParent();
_enemies.erase(f);
}
}
return true;
};
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
...
this->schedule(schedule_selector(HelloWorld::moveEnemies), 1.0f);
...
}
void HelloWorld::moveEnemies(float f) {
for (unsigned i = 0; i < _enemies.size(); i++)
{
cocos2d::Vec2 pos = _enemies.at(i)->getPosition(); //This line throws the exception
_enemies.at(i)->setPosition(pos.x + 5, pos.y);
}
}
I have an event that when I press a key, creates a sprite and if it collides with one of the enemies just disappears, everything is fine here, very simple, but when you put the "moveEnemies" function and the sheduler everything is complicated. .. by shooting at one of the enemies quickly throws me an exception (Visual Studio 2015):
"Access violation when reading location 0xDDDDDE35"
I think this occurs because is the vector manipulated at the same time by the event and the "moveEnemies" function?
Is it possible to solve this? Or am I doing something wrong? I would appreciate anyone guiding me ...
I found a simple solution, it is possible that someone will serve you ...
We simply add a scheduler to each sprite and the vector would become obsolete.
auto enemy = cocos2d::Sprite::createWithSpriteFrameName("x.png");
enemy->setPosition(x, y);
enemy->setScale(0.5, 0.5);
//bla bla bla...
enemy->schedule([enemy](float x) {
//Code
auto pos = enemy->getPosition();
enemy->setPosition(pos.x + 10, pos.y);
}, 2.0f, "tag");
It is assumed that deleting the sprite in the event would eliminate the scheduler ...
Related
i try to making a little game in SFML (C++) So i have a simple Problem with pick up an object by a player. The object is in a vector, i have to objects on the screen and when the player intersect(Collision) with them they have to erase. But still booth objects erase when the player intersect with a single object, how i can fix ?
so my question is: How can i erase (or delete) a single object from a vector who is collide with a player?
here is my code
RectangleShape object;
std::vector<RectangleShape> objects(3);
RectangleShape player;
...
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event));
...
for (int j = 0; j < objects.size(); j++){
objects[j].setFillColor(Color::Cyan);
objects[1].setSize(Vector2f(100, 100));
objects[1].setPosition(Vector2f(650, 1000));
objects[2].setSize(Vector2f(100, 100));
objects[2].setPosition(Vector2f(650, 700));
}
for (auto& object: objects){
FloatRect playerBounds = player.getGlobalBounds();
FloatRect objectBounds = object.getGlobalBounds();
for ( auto it = objects.begin(); it != objects.end();)
{
if(objectBounds. intersects(playerBounds)) {
it = objects.erase(it);
}
else
{
++it;
}
window.draw(objects[1]);
window.draw(objects[2]);
}
}
I tried to keep as much from your code as it was possible so you could see the difference, not the whole different code. The reason why your code wasn't deleting just one obj. and every single is that your loops weren't correctly written.
See this:
for (auto& object: objects){ //iterating through objects
FloatRect playerBounds = player.getGlobalBounds();
FloatRect objectBounds = object.getGlobalBounds(); //<- this *1*
for ( auto it = objects.begin(); it != objects.end();) //iterating through every single one AGAIN
{
if(objectBounds. intersects(playerBounds)) { //what this if does is that it check if this *1* obj. intersects with your player
it = objects.erase(it); //however it doesn't particularly erase that *1* obj., it just erases every obj. in the vector, because when that if is true it will iterate through the vector and delete one by one.
}
else
{
++it;
}
}
The main problem is that 2nd iteration for ( auto it = objects.begin(); it != objects.end();) and if in it are 2 separate things.
There are many solutions to make it work, but then it would be just adding more code to make more spaghetti, so just redo it to make it better.
Here is the working ver:
RectangleShape object;// not needed
std::vector<RectangleShape> objects(3);
for (int j = 0; j < objects.size(); j++) {
objects[j].setFillColor(Color::Cyan);
objects[j].setSize(Vector2f(100, 100));
objects[j].setPosition(Vector2f(650, 1000 - j * 300)); //just a quick way to set position of "every" to be 1000, 700, 400 etc.
//objects[1].setSize(Vector2f(100, 100));
//objects[1].setPosition(Vector2f(650, 1000)); // shouldn't set the obj[1] parameters withing loop since you are setting them the n times where n is number of objs. in the vector
//objects[2].setSize(Vector2f(100, 100));
//objects[2].setPosition(Vector2f(650, 700));//and you should just delete it from there
}
RectangleShape player;
player.setSize(Vector2f(100, 100));
player.setPosition(Vector2f(650, 1000)); //setting player parameters separately since it is not in the vector
// Also I moved making and setting of player and other obj in the vector out of the game loop so it won't be creating them every single frame
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event));
...
for (auto it = objects.begin(); it != objects.end();) {
FloatRect playerBounds = player.getGlobalBounds();
FloatRect objectBounds = it->getGlobalBounds(); //iterator is just similar to a pointer, so to access what it points to you need to use "->" or "*" instead of "."
if (objectBounds.intersects(playerBounds)) {
it = objects.erase(it);
}
else
it++;
}
for (auto& object : objects) { //drawing every single obj. in the vector
window.draw(object);
}
window.draw(player); //drawing player separately since it is not in the vector
}
For the tutorial part that you mentioned in the comments, I would recommend you checking ones from sfml site since they not only contain descriptions of methods, etc. but also demo code.
Firstly, while not new to programming, I am very new to C++, so please bear with me.
I am using the Raylib library to attempt making a particle system for a game.
This consists of a struct with a few private members and public functions:
struct Particle {
Particle() {
mPosVector = {(float)GetMouseX(), (float)GetMouseY()};
mVelVector = {(float)GetRandomValue(15, 70)/100, (float)GetRandomValue(15, 70)/100};
mSize = GetRandomValue(5, 15);
}
void update(double deltaTime) {
mPosVector.x += mVelVector.x;
mPosVector.y += mVelVector.y;
}
void draw() {
DrawRectangleV(mPosVector, {(float)mSize, (float)mSize}, WHITE);
}
private:
Vector2 mPosVector;
Vector2 mVelVector;
int mSize;
};
The Vector2 type is defined by Raylib:
struct Vector2 {
float x;
float y;
};
In my main function I have an std::vector storing Particles. A particle gets added when the left mouse button is pressed. I loop through the Particles vector twice, once for updating position based on velocity and once for drawing. I was originally doing these both in one loop, but was still getting the problem that I will get onto, so tried it this way.
This is the current code:
std::vector<Particle> particles = {Particle()};
while (!WindowShouldClose()) {
deltaTime = GetFrameTime();
if (IsMouseButtonDown(0)) {
particles.push_back(Particle());
}
for (Particle part : particles) {
part.update(deltaTime);
}
BeginDrawing();
ClearBackground(BLACK);
DrawFPS(10, 10);
DrawText((numToString<double>(deltaTime*1000).substr(0, 5) + "ms").c_str(), 10, 40, 20, WHITE);
for (Particle part : particles) {
part.draw();
}
EndDrawing();
So, my problem: While particles are being instantiated as expected while pressing the left mouse button and being drawn, for some reason their positions are not being updated by their velocity. I have tried printing debug information to the console, such as the velocity, and it is as expected, but for some unknown reason to me (probably just me being stupid) their positions aren't being updated.
Any help would be greatly appreciated.
for (Particle part : particles) {
part.update(deltaTime);
}
this is making a copy of each entry , you need
for (Particle &part : particles) {
part.update(deltaTime);
}
to get a reference to the object in the vector to update it in place
To understand, think that the ranged for is just short hand for this
for(int i = 0; i < particles.size(); i++)
{
// this line copies the value
particle p = particles[i];
}
whereas the one with & in it does
for(int i = 0; i < particles.size9); i++)
{
// this line gets a reference to the ith entry
particle &p = particles[i];
}
Its nothing special to do with the ranged for loop.
I am writing a game on OpenGL + C++.
The only way to generate animation is define an infinite loop inside glutDisplayFunc. And I did. But imagine I have different gameObjects, and each one has its draw() function.
But I want a function to add another object. As I hold them in array of pointers, this function looks like this:
void display() {
while (running) {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaque
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer
if(objects_count != 0)
for(unsigned int i = 0; i< objects_count - 1; i++) {
(objects + i)->drawAsPoint();
}
glFlush();
usleep(f60FPS);// Render now
}
}
bool addRenderableObjectSync(GameEntity *object) {
running = false;
if(objects_count + 1>= objects_capacity) {
//reallocating memory
objects_capacity = round(objects_capacity * 1.5f);
GameEntity * _new_objects = new GameEntity[objects_capacity];
for(unsigned int i = 0; i < objects_count; i++) {
_new_objects[i] = objects[i];
}
//now objects are safe to delete free
delete[] objects;
//now we move new array to objects and free the temporary one
objects = _new_objects;
delete[] _new_objects;
}
objects[objects_count] = *object;
objects_count++;
//delete *object as it no longer needed;
delete object;
return true; }
running is a boolean which responses for loop inside display func.
BUT, draw-loop is infinite, i think that i need some async function which can hack running state, for example this:
void Renderer::addRenderableObject(GameEntity *object) {
//the reason we do it cause a game is in infinite loop which never ends
//so we stop the game, add an object, and after fn finish we let it run again
std::future<bool> fut = std:: async(addRenderableObjectSync, object);
running = fut.get();
}
Is that a right solution? Or there are better approaches?
In my gaming context, the sprite is a smiling baby, when the baby is touched, the current sprite image changed to a crying baby, then after 3 seconds of crying sound effect ended, the sprite will restore to the smiling baby image.
My problem is
How to swap back with the previous image?
I have changed the smiling baby to a crying one, but i have no idea how could I swap it with the original smiling one?
How to ensure one click at one time?
Each time the baby sprite is touched, the audio will start to play which is not ideal, because I hope the event function is only called after the previous event process is finished.
here is my code, and thank you a lot!
bool HelloWorld::init()
{
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//add background scene
auto backgroundSprite = Sprite :: create("scene001.jpg");
backgroundSprite->setAnchorPoint(Vec2(0,0));
backgroundSprite->setScaleX((visibleSize.width / backgroundSprite->getContentSize().width) * 1);
backgroundSprite->setScaleY((visibleSize.height / backgroundSprite->getContentSize().height) * 1);
addChild(backgroundSprite);
//add smileBaby sprite
auto smileBabySprite = Sprite :: create("figure001.png");
smileBabySprite -> setPosition(Vec2(500,400));
addChild(smileBabySprite);
//add crying audio
auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
auto babyListener = EventListenerTouchOneByOne::create();
babyListener -> onTouchBegan = [smileBabySprite, audio](Touch* touch, Event* event)
{
//change smileBaby sprite to cryingBaby sprite
smileBabySprite->setTexture(CCTextureCache::sharedTextureCache()->addImage("figure002.png"));
audio -> playEffect("babycry.mp3",false,1.0f,1.0f,1.0f);
return true;
};
babyListener -> onTouchEnded=[smileBabySprite](Touch* touch, Event* event )
{
};
_eventDispatcher -> addEventListenerWithSceneGraphPriority(babyListener, this);
return true;
}
What you want is to keep state about if the baby is crying or not. The best thing to do is keep this logic in a custom Node subclass.
Here is some (almost pseudo-)code to get you started:
Baby.h:
#pragma once
#include "cocos2d.h"
class Baby : public cocos2d::Node
{
private:
cocos2d::Sprite *_sprite; // Weak reference
bool _crying;
float _cryingTime;
public:
CREATE_FUNC(Baby);
protected:
virtual bool init() override;
virtual void update(float delta) override;
public:
void touched();
bool isInside(cocos2d::Touch *touch) const
protected:
void setSprite();
};
Baby.cpp:
#include "Baby.h"
USING_NS_CC;
bool Baby::init()
{
if (!Node::init())
return false;
_crying = false;
setSprite();
scheduleUpdate();
return true;
}
void Baby::update(float delta)
{
Node::update(delta);
if (_crying) {
_cryingTime -= delta;
if (_cryingTime <= 0.0f) {
_crying = false;
setSprite();
}
}
}
void Baby::touched()
{
if (_crying)
return; // Already crying
_crying = true;
_cryingTime = 3.0f; // Length of audio, I guess?
setSprite();
// Start crying sound here
}
bool Baby::isInside(Touch *touch) const
{
Vec2 locationInNode = _sprite->convertToNodeSpace(touch->getLocation());
Size size = _sprite->getContentSize();
Rect rect = Rect(0.0f, 0.0f, size.width, size.height);
return rect.containsPoint(locationInNode);
}
void Baby::setSprite()
{
if (_sprite)
_sprite->removeFromParent();
_sprite = Sprite::initWithFile(_crying ? "baby_crying.png" : "baby.png");
Vec2 size = getContentSize();
_sprite->setPosition(size.width * 0.5f, size.height * 0.5f);
addChild(_sprite);
}
You add the Baby node in the parent instead of the sprite, using:
_baby = Baby::create();
_baby->setPosition(Wherever);
addChild(_baby);
Where _baby is an instance variable and use the isInside() method to test if a touch event is within the bounds of the sprite and call its touched() method:
Touch *touch = ...;
if (_baby->isInside(touch)) {
_baby->touched();
}
and the Baby object will ignore the touch depending on state.
I'm just training cocos2d-x.
I tryed to show sprite animation,but EXC_BAD_ACCESS error showed.
I wrote code as follows.
However,when I wrote the "animation" function to the inside of "init" function ,EXC_BAD_ACCESS error didn't show.
What is wrong?
GameScene.h
#include "cocos2d.h"
#include "SimpleAudioEngine.h"
class GameScene : public cocos2d::CCLayer
{
public:
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::CCScene* scene();
// implement the "static node()" method manually
CREATE_FUNC(GameScene);
void animation();
cocos2d::CCSprite* pPlayer;
//スプライトを格納する領域
cocos2d::CCSpriteFrame* pSprites[12];
};
GameScene.m
bool GameScene::init()
{
if(!CCLayer::init())
{
return false;
}
//端末サイズ取得
CCSize visiblesize = CCDirector::sharedDirector()->getVisibleSize();
//背景画像
CCSprite* bgImg = CCSprite::create("pic1.jpg");
//背景画像のポジション
bgImg->setPosition(ccp(visiblesize.width / 2, visiblesize.height / 2));
//背景画像のポジションを取得
CCPoint pos = bgImg->getPosition();
//===============================================================================
// ボタンイベント作成
//ボタンイベント追加
CCMenuItemImage *tapitem = CCMenuItemImage::create("animButton.png", "animButton.png", this, menu_selector(GameScene::animation));
CCMenu* tapMenu = CCMenu::create(tapitem, NULL);
tapMenu->setPosition(ccp(bgImg->convertToNodeSpace(pos).x, bgImg->convertToNodeSpace(pos).y));
//ボタン画像設置
bgImg->addChild(tapMenu);
//背景画像設置
this->addChild(bgImg);
cocos2d::CCSpriteFrame* pSprites[12];
const int WIDTH_SIZE = 96; //1つのスプライトの幅
const int HEIGHT_SIZE = 64; //1つのスプライトの高さ
//●アトラスから矩形切り出し、スプライト領域に格納.アニメーション画像を扱いたい時は、pSpriteから取り出してください。
for(int y = 0; y < 4; y++){
for(int x = 0; x < 3; x++){
CCRect rect(x * WIDTH_SIZE, y * HEIGHT_SIZE,WIDTH_SIZE,HEIGHT_SIZE);
pSprites[y * 3 + x] = CCSpriteFrame::create("texture1.png",rect);
}
}
// プレイヤースプライトを生成
pPlayer= CCSprite::create("texture1.png", CCRectMake(0, 64, 96, 64));
// プレイヤーのポジション
pPlayer->setPosition(ccp(100,100));
//プレイヤーをシーンに登録して
bgImg->addChild(pPlayer);
return true;
}
void GameScene::animation()
{
//移動先の指定
CCMoveTo* move = CCMoveTo::create(1.8, ccp(200, 100));
//===============================================
// アニメーションの作成
CCAnimation* animation = CCAnimation::create();
for(int i=3;i<6;i++){
animation->addSpriteFrame(pSprites[i]);
}
//アニメーションの設定:1コマ0.1秒で切り替える
animation->setDelayPerUnit(0.1);
//アニメーションの設定:6回ループさせる
animation->setLoops(6);
CCRepeatForever *pAction = CCRepeatForever::create( CCAnimate::create(animation) );
// 作成したアニメーションを実行
pPlayer->runAction(pAction);
pPlayer->runAction(move);
}
well, it didn't let me leave a comment as i don't have 50 rep... where exactly is this error coming up? The line i mean?
Is it this code piece?
for(int i=3;i<6;i++){
animation->addSpriteFrame(pSprites[i]);
}
if yes, then please make sure that the "pSprites[i]" hasn't been released... If that is the case please create it with "new" and then perform a clean up by yourself.
The reason this could be happening is because there is no one referencing these sprites inside the "init" function... and when the flow comes out of "init" function, these values get released (as they are created with CREATE which sets autorelease by default)
Again, this is my assumption, looking at the code...
do give your feedback!