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!
Related
Good morning.
Im learning some concepts about inheritance and consoles manipulation.
Im pretty beginner as you could see.
So Im trying to have a single character drawn on a console and I want its position to be updated.
Now please note that I know my code is probably very bad in multiple ways and that there are probably hundreds better completely alternative ways to do this, but I want to understand some inheritance concepts first and why it doesn't work the way it is.
So, I draw my player character "X" on the console, then I update its position calling a specific member method to move it.
Now, because I made it that Player class expand DrawConsole class, I would like to call drawConsole on the Player instance.
When I do this, I have that playerA instance have its position coordinates actually updated, but the reference to the player instance have now two member called 'position', as you can see on the image.
How can I say to choice the playerA one without completely remake the code or use a completely different approach?
Or maybe simply I cant and I have actually complete change the approach?
Hope I was able to comunicate what my doubt actually is.
Here is the code
#include <ctime>
#include <cstdlib>
#include "windows.h"
#define width 100
#define height 15
class StaticBuffer
{
public:
StaticBuffer() { srand(time(0)); }
void loadBackGround(CHAR_INFO *backGround, int swidth, int sheight)
{
for (int y = 0; y < sheight; y++)
{
int rnd = rand() % 100 + 1;
for (int x = 0; x < swidth; x++)
if (y == 0 || y == sheight - 1)
{
backGround[y * swidth + x].Char.AsciiChar = (unsigned char)127;
backGround[y * swidth + x].Attributes = (unsigned char)23;
}
else if (x > 4 * rnd && x < (4 * rnd) + 5 || x > 4 * rnd / 2 && x < (4 * rnd / 2) + 5)
{
backGround[y * swidth + x].Char.AsciiChar = (unsigned char)178;
backGround[y * swidth + x].Attributes = (unsigned char)12;
}
else
{
backGround[y * swidth + x].Char.AsciiChar = 32;
backGround[y * swidth + x].Attributes = (unsigned char)3;
}
}
}
private:
};
class DrawConsole
{
public:
DrawConsole()
{
wConsole = GetStdHandle(STD_OUTPUT_HANDLE);
rConsole = GetStdHandle(STD_INPUT_HANDLE);
windowSizeInit = {0, 0, 30, 10};
windowSize = {0, 0, bufferSize.X - 1, bufferSize.Y - 1};
backGround = new CHAR_INFO[bufferSize.X * bufferSize.Y];
obstacle = new CHAR_INFO[bufferSize.X * bufferSize.Y];
inputBuffer = new INPUT_RECORD[4];
drawBackGround.loadBackGround(backGround, bufferSize.X, bufferSize.Y);
nInputWritten = 0;
nOutputWritten = 0;
playerString[0] = L'X';
charLenght = 1;
position = {10,13};
}
void drawConsole()
{
wConsole = GetStdHandle(STD_OUTPUT_HANDLE);
rConsole = GetStdHandle(STD_INPUT_HANDLE);
SetConsoleWindowInfo(wConsole, TRUE, &windowSizeInit);
wConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleScreenBufferSize(wConsole, bufferSize);
SetConsoleWindowInfo(wConsole, TRUE, &windowSize);
WriteConsoleOutputA(wConsole, backGround, bufferSize, {0,0}, &windowSize);
WriteConsoleOutputCharacterW(wConsole, playerString, charLenght, position, &nOutputWritten);
}
void drawChar()
{
WriteConsoleOutputA(wConsole, backGround, bufferSize, {0,0}, &windowSize);
WriteConsoleOutputCharacterW(wConsole, playerString, charLenght, position, &nOutputWritten);
}
protected:
HANDLE wConsole;
HANDLE rConsole;
COORD bufferSize{width, height};
SMALL_RECT windowSizeInit;
SMALL_RECT windowSize;
CHAR_INFO *backGround;
CHAR_INFO *obstacle;
INPUT_RECORD *inputBuffer;
DWORD nInputWritten;
DWORD nOutputWritten;
DWORD charLenght;
StaticBuffer drawBackGround;
wchar_t playerString[2];
COORD position;
};
class Player :public DrawConsole
{
public:
Player()
{
position.X = 20;
position.Y = height - 2;
}
void movePlayerRight()
{
for (int i = 0; i < 3; i++)
position.X += 1;
}
COORD getPositionC() { return position; }
private:
COORD position;
};
Player *playerA = new Player;
DrawConsole *myConsole = new DrawConsole;
int main()
{
myConsole->drawConsole();
while (true)
{
//Sleep(5000);
playerA->movePlayerRight();
playerA->drawChar();
}
}
It depends on what you really want. If the idea is that both variables represent the same concept, you shouldn't have to re-define it in the derived class, because it is "protected" in the base class so the derived class is able to access it.
If the variables represent different things, but they happen to have the same name (which, by the way, would be a bad idea), you can qualify it with the class the variable has been defined in. So, for instance, you could do:
DrawConsole::position.X += 1;
To modify the position variable declared in DrawConsole and:
Player::position.X += 1;
To modify the position variable declared in Player
But, as I said before, I would try to avoid having two variables with the same name because it can easily result in errors.
UPDATE:
If you want to maintain the inheritance as is, just remove the attribute position from Player. The reason is as follows:
Currently, when you call drawChar, you are executing code that is in the DrawConsole class (Player itself does not define a drawChar method). This code cannot access Player::position because a method in a parent class cannot access an attribute in a child class (even if you are calling the method from an instance of the child class), so it only sees DrawConsole::position and that is the variable that it is using.
But when you call movePlayerRigth in an instance of Player, the code that is being executed is a method in the Player class. This method tries to access a position attribute and it finds out that there are two possibilities: DrawConsole::position and Player::position. In this case, it chooses Player::position because it is defined in the same class.
So, you have a method that draws the console based on DrawConsole::position and another method that modifies Player::position. This can't work and in fact if you run it, you will see that the X is not moving.
If you remove the position variable from Player, in movePlayerRight when you try to access the variable position, the code will see that Player does not define a position attribute, but it realizes that its parent class (DrawConsole) does indeed define a position attribute, and with protected access. Being protected means that code in child classes can access it directly, and so movePlayerRight will modify DrawConsole::position. In this case, both drawChar and movePlayerRight will access the same variable and it will work as expected.
So, if you want it this way, remove from the Player class the line:
COORD position;
And you will see that the code compiles and works as expected (the X moves right) because now the code in Player and the code in DrawConsole are accessing both the same variable (DrawConsole::position).
I have created a CellGrid class for my cellular automaton, and I want to draw that grid using Cairo. The cell_grid.cpp code snippet below is my implementation of the on_draw() function override.
For this example, I have set width_ = height_ = 50 and cell_buffer_ is an std::vector with a size of width_ * height_. The vector containts instances of the Cell enum class, which is defined in the cell_grid.hpp snippet below.
The problem is that for some reason, already when drawing just 50*50 or 2 500 rectangles, I get about 2fps. This Cairo implementation is actually a rewrite of an SFML implementation, on which I got about 150fps when drawing 200*100 or 20 000 rectangles. But as far as I know, SFML isn't a viable option in combination with GTK.
cell_grid.hpp snippet
class CellGrid : public Gtk::DrawingArea
{
public:
enum class Cell : uint8_t //uchar
{
Dead = 0,
Alive = 1
};
// ...
};
cell_grid.cpp snippet
// ...
bool CellGrid::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
const Gtk::Allocation allocation = get_allocation();
const double cell_w = allocation.get_width() / (double)width_;
const double cell_h = allocation.get_height() / (double)height_;
for(size_t y = 0; y < height_; y++)
{
for(size_t x = 0; x < width_; x++)
{
cr->set_line_width(5.0);
cr->rectangle(x * cell_w, y * cell_h, cell_w, cell_h);
cr->set_source_rgb(0.5, 0.5, 0.5);
cr->stroke_preserve();
cell_buffer_[y * width_ + x] == Cell::Alive ?
cr->set_source_rgb(1.0, 1.0, 1.0) :
cr->set_source_rgb(0.1, 0.1, 0.1);
cr->fill();
}
}
return true;
}
// ...
Thanks in advance!
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 ...
I am trying to make a sprite as a frog which will come from the top of the screen and will go downwards to the bottom at y axis =0.Its working fine as a normal CCMoveTo but i want that after a jump the frog should rest for 1 second then again jump.Some kind of delay in moving.Can anyone tell me with this.I am attaching my code also.
my frog animations are from fly1.png to fly5.png.I just want a delay after each move or we can say that I just want to call the CCMove after 1 second delay each time until the frog reaches the y axis=0
Any help will be appreciated.Thanks
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
using namespace cocos2d;
using namespace CocosDenshion;
CCScene* HelloWorld::scene()
{
CCScene *scene = CCScene::create();
HelloWorld *layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}
bool HelloWorld::init()
{
if ( !CCLayer::init() )
{
return false;
}
CCSize winSize=CCDirector::sharedDirector()->getWinSize();
_bgNode = CCNode::create();
_bgNode->setPosition(ccp(winSize.width/2, winSize.height/2));
this->addChild(_bgNode, -1);
_bgSprite = CCSprite::create("bg_2.jpg");
_bgNode->addChild(_bgSprite);
float rX = winSize.width/_bgSprite->getContentSize().width;
float rY = winSize.height/_bgSprite->getContentSize().height;
_bgNode->setScaleX(rX);
_bgNode->setScaleY(rY);
z=CCSprite::create("fly1.png");
z->setScaleX(rX);
z->setScaleY(rY);
z->setPosition(ccp(winSize.width/2,winSize.height+1));
this->addChild(z);
CCAction *a=CCRepeatForever::create(HelloWorld::getAnimationWithFrames(1,5));
z->runAction(a);
z->runAction(CCSequence::create(CCMoveTo::create(2.0, ccp(winSize.width/2, 0)), CCCallFuncN::create(this, callfuncN_selector(HelloWorld::setInvisible)), NULL));
return true;
}
cocos2d::CCAnimate* HelloWorld::getAnimationWithFrames(int from, int to)
{
CCArray* frames = CCArray::create();
for (int i = from; i <= to; i++)
{
CCString *str = CCString::createWithFormat("fly2%d.png", i);
CCSpriteFrame *f = CCSpriteFrame::create(str->getCString(), CCRect(0,0,256,400));
frames->addObject(f);
}
//(frames,speedofmovementofanimation);
CCAnimation *animation = CCAnimation::createWithSpriteFrames(frames,0.15f);
CCAnimate *a = CCAnimate::create(animation);
return a;
}
void HelloWorld::setInvisible()
{
this->removeChild(z,true);
}
Firstly, you have to create a frogJump() function. Then,in frogJump() function add the following code:
void HelloWorld::frogJump()
{
CCJumpTo* jumpTo = CCJumpTo::create(1,ccp(x/6,y/8),y/1.2f,1);
z->runAction(jumpTo);
}
Then, in the init() function add the following line:
this->scheduleOnce(SEL_SCHEDULE(&HelloWorld::frogJump),0);
this->schedule(SEL_SCHEDULE(&HelloWorld::frogJump),2);
The code below compiles and runs well in the init() section of the class, yet when I try to create a separate method for it, a CCSpriteFrame ended up null, I'd like to learn what conceptual assumption I gotten myself into this time =s
void SceneView::runZoo(Animal& animal, std::string attack) {
//
// Animation using Sprite BatchNode
//
std::string spriteName;
CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
CCSize iSize = CCDirector::sharedDirector()->getWinSize();
spriteName = "killerRabbit.plist";
cache->addSpriteFramesWithFile(spriteName.c_str());
spriteName = "killerRabbit1.png";
sprite = CCSprite::createWithSpriteFrameName(spriteName.c_str());
sprite->setPosition( ccp(iSize.width/2 - 160, iSize.height/2 - 40) );
spriteName = "killerRabbit.png";
CCSpriteBatchNode* spritebatch = CCSpriteBatchNode::create(spriteName.c_str());
spritebatch->addChild(sprite);
this->addChild(spritebatch);
CCArray* animFrames = CCArray::createWithCapacity(15);
spriteName = "killerRabbit";
char str[100] = {0};
for(int i = 1; i <= 9; i++) {
sprintf(str, (spriteName + "%d.png").c_str(), i);
CCSpriteFrame* frame = cache->spriteFrameByName(str);
//Null here
animFrames->addObject(frame);
//Null here
}
CCAnimation* animation = CCAnimation::createWithSpriteFrames(animFrames, 0.15f);
sprite->runAction( CCRepeatForever::create(CCAnimate::create(animation)) );
}
The actual error:
/** Appends an object. Behavior undefined if array doesn't have enough capacity. */
void ccArrayAppendObject(ccArray *arr, CCObject* object)
{
CCAssert(object != NULL, "Invalid parameter!");
object->retain();
arr->arr[arr->num] = object;
arr->num++;
}
That means that the CCSpriteFrameCache might be nulled for some reason, any ideas?
I havent worked much with cocos2d in a C++ environment but I know in Objective-c before you can use an array you have to do this
CCArray *array = [[CCArray alloc] initWithCapacity:15];
If you do not do that it will always be null. I would assume it is the same with C++
frame == null when the specified image not exists. I think you'd better look through plist file or locate which frame caused error.