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.
Related
I'm quite confused about the reasoning behind the behaviour of pointers to a variable. I would have thought that if I append a pointer to a vector and access it, whether I changed the pointer itself it should still work the same upon the same variable. What I mean by this is, for example, I have a vector of integer pointers and I modify a variable defined somewhere else that has been appended to the vector. If I was to then print them, it should update the vector (not in reality) but it should print the new value of an integer. I'm trying to apply this to an SDL_Texture* in SDL2, however it doesn't quite make sense. In summary, the same concept is applying there however I am using Textures instead. I modify the texture and do "things" with it, but at the end of the loop when I render it, the vector is still iterating to the SDL_Texture* appended to it anyway. What my problem is, is as I change and modify the texture, when I go to render it it doesn't show up. This isn't because the texture isn't properly loaded or anything (I have tested it and rather than using the vector, I draw it raw) but when using the vector it doesn't work properly. Here is the code:
void Main::Mainloop()
{
Screen_Object m_Screen_Object;
TTF_Font* font = TTF_OpenFont("Anonymous_Pro.ttf", 30);
SDL_Surface* surf = TTF_RenderText_Blended(font, "Starting fps", {0,0,0});
SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
SDL_FreeSurface(surf);
SDL_Rect rct = {20, 100, 0,0};
SDL_QueryTexture(tex, NULL, NULL, &rct.w, &rct.h);
m_Screen_Object.Add_Texture(tex);
Uint32 start, finish, counter;
counter = 0;
start = SDL_GetTicks();
finish = SDL_GetTicks();
bool running = true;
while (running)
{
Events::Event_Loop();
if (Events::Quit_Application()){
running = false;
break;
}
///Clear display to color
SDL_SetRenderDrawColor(Render::Get_Renderer(), 0,255,0,255);
SDL_RenderClear(Render::Get_Renderer());
///Do stuff here
m_Screen_Object.Draw_Textures();
finish = SDL_GetTicks();
counter += 2;
if (finish - start >= 500)
{
start = SDL_GetTicks();
SDL_DestroyTexture(tex);
std::string fps = std::to_string(counter);
surf = TTF_RenderText_Blended(font, fps.c_str(), {0,0,0});
tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
SDL_FreeSurface(surf);
SDL_QueryTexture(tex, NULL, NULL, &rct.w, &rct.h);
counter = 0;
}
SDL_RenderPresent(Render::Get_Renderer());
}
SDL_DestroyTexture(tex);
TTF_CloseFont(font);
}
int main(int argc, char* argv[])
{
Main::Mainloop();
return 0;
}
and here is the declaration of Screen_Object:
In the header:
std::vector < SDL_Texture* > m_Textures;
In the .cpp:
void Screen_Object::Add_Texture(SDL_Texture* p_Texture)
{
m_Textures.push_back(p_Texture);
}
void Screen_Object::Draw_Textures()
{
for (unsigned int i=0; i < m_Textures.size(); i++)
{
SDL_RenderCopy(Render::Get_Renderer(), m_Textures[i], NULL, &m_Rect);
}
}
Now this code doesn't work in the way I believe it should since I can't understand why it isn't working, but when I change the vector's type to be SDL_Texture**, the code works fine. What on earth is wrong with the code without the **, I just can't logically understand why it won't work properly
The issue seems to be that you are storing pointers in the vector, but outside the vector you are invalidating the pointer.
void Screen_Object::Add_Texture(SDL_Texture* p_Texture)
{
m_Textures.push_back(p_Texture);
}
void Main::Mainloop()
{
Screen_Object m_Screen_Object;
//...
SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
//...
m_Screen_Object.Add_Texture(tex); // <-- add pointer to vector
//...
tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf); // <-- This changes the pointer, but has no effect on the vector's
//...
So anything after that line, if you access the pointer in m_Textures vector, that pointer in the vector is no longer valid, or worse, it is valid, but points to an old (but still valid) SDL_Texture.
A very simple solution is to ensure that you use the pointer you stored in the m_Textures by obtaining a reference to that pointer:
void Main::Mainloop()
{
Screen_Object m_Screen_Object;
TTF_Font* font = TTF_OpenFont("Anonymous_Pro.ttf", 30);
SDL_Surface* surf = TTF_RenderText_Blended(font, "Starting fps", {0,0,0});
SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
m_Screen_Object.Add_Texture(tex); // <-- Immediately do this
auto& texRef = m_Screen_Object.back(); // <-- Get a reference to the pointer.
Then you use texRef after that. This is the actual reference to the pointer you added to the vector, and not a copy of the pointer.
Then the loop would be simply:
texRef = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
This changes the actual pointer stored in the vector.
I'm on a project currently, that implements a quadTree, it's just a visualization of the algorithm actually, but when I run the code, I see the memory usage increasing more and more,
now I am new to c++, but I think optimal resource management is when memory usage is constant right ?
here is how it is implemented:
QuadTree::QuadTree(Rectangle rec, int capacity ,const char* name ) {
this->rec = new Rectangle();
*this->rec = rec;
this->capacity = capacity;
this->nbPoints = new int ;
*this->nbPoints = 0;
this->divided = new bool;
*this->divided = false;
this->name = name;}
QuadTree::~QuadTree() {
delete this->nbPoints;
delete this->divided;
delete this->ne;
delete this->se;
delete this->nw;
delete this->sw;
delete this->rec;}
here are the quadtree constructor and destructor , (you can see that i respect the "new = delete" rule for optimal resource management )
when a point is inserted , it checks on which section it belongs and if that section is subdivided , if so that check is recursively called for each subdivision and so on, as you can see :
class QuadTree {
private:
int* nbPoints;
Rectangle* rec;
bool* divided;
QuadTree* nw = NULL;
QuadTree* ne = NULL;
QuadTree* sw = NULL;
QuadTree* se = NULL;
const char* name;
each node of the quadtree has a pointer to eventual subdivisions and this memory is alocated dynamicaly i believe ( if i'm saying wrong stuff feel free to correct me please and i apologize in advance if my post does not belong in here)
anyway, i use SDL to visualize and see what is hapenning here here is the main :
while (!stop) {
Start = SDL_GetTicks();
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT)
{
stop = true;
}
}
Rectangle* rect = NULL;
rect = new Rectangle(0, 0, 800, 700);
QuadTree* test = NULL;
test = new QuadTree(*rect, 10);
for (int i = 0; i < 1000; i++)
{
Point P(rand() % (int)__WIDTH, rand() % (int)__HEIGHT);
//std::cout << P.GetX() << "," << P.GetY() << std::endl;
test->Insert(P);
}
test->draw(window, Renderer);
SDL_RenderPresent(Renderer);
cap_framerate(Start);
delete test;
delete rect;
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 1);
SDL_RenderClear(Renderer);
}
as you can see i create and destroy a quadtree each frame (this model is to implement moving points later on so i can destroy an obsolete tree and build another with updated coords)
it works quite fine with 1000 points i run it at apriximately 35 fps so im happy with that , however i get an growig graph of memory usage :
That gets me wondering if I properly delete everything I allocate dynamically with the new pointer.
fell free to consult this git https://github.com/lucipher128/QuadTree for reproduction
I have an Entity.h like this:
using namespace physx;
class Entity
{
public:
Entity(Ogre::Vector3 dims, Ogre::Vector3 pos, std::string mesh, std::string id);
virtual ~Entity(void);
virtual void update(Ogre::Real dt);
virtual void init(Ogre::SceneManager* sceneMgr, PxPhysics* physics, PxScene* scene, PxVec3 velocity=PxVec3(0, 0, 0));
protected:
Ogre::Entity* mOgreEntity = NULL;
Ogre::SceneNode* mOgreNode = NULL;
Ogre::Vector3 mPosition;
Ogre::Vector3 mDimensions;
std::string mMesh;
std::string mId;
PxRigidDynamic* mActor;
PxMaterial* mMaterial;
};
And here is my Entity source:
#include "Entity.h"
Entity::Entity(Ogre::Vector3 dims, Ogre::Vector3 pos, std::string mesh, std::string id)
{
mDimensions = dims;
mPosition = pos;
mMesh = mesh;
mId = id;
mActor = NULL;
mMaterial = NULL;
}
Entity::~Entity(void)
{
}
void Entity::update(Ogre::Real dt)
{
PxVec3 pos = mActor->getGlobalPose().p;
Ogre::Real r = 0;
mOgreNode->setPosition(Ogre::Vector3(pos.x + r, pos.y + r, pos.z + r));
}
void Entity::init(Ogre::SceneManager* sceneMgr, PxPhysics* physics, PxScene* scene, PxVec3 velocity)
{
// Create an Entity
mOgreEntity = sceneMgr->createEntity(mId, mMesh);
mOgreEntity->setCastShadows(true);
// Create a SceneNode and attach the Entity to it
mOgreNode = sceneMgr->getRootSceneNode()->createChildSceneNode(mId + "Node");
Ogre::AxisAlignedBox box = mOgreEntity->getBoundingBox();
Ogre::Vector3 realSizes = box.getSize();
mOgreNode->setPosition(mPosition);
mOgreNode->attachObject(mOgreEntity);
Ogre::Vector3 scaler = Ogre::Vector3(mDimensions.x / realSizes.x, mDimensions.y / realSizes.y, mDimensions.z / realSizes.z);
mOgreNode->scale(scaler);
mMaterial = physics->createMaterial(1.5f, 1.5f, 1.0f);
PxGeometry* geometry = NULL;
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = &g;
} else {
// geometry = NULL;
}
PxTransform transform = PxTransform(PxVec3(mPosition.x, mPosition.y, mPosition.z));
mActor = PxCreateDynamic(*physics, transform, *geometry, *mMaterial, PxReal(.1));
// if(!mActor) {
// MessageBox( NULL, "no actor", "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
// return;
// }
mActor->setLinearVelocity(velocity);
// And add the actor to a scene:
scene->addActor(*mActor);
}
Now, if I create a single entity and initialize it works. Even wirh a second entity var it works as well. Now with an array:
Entity *mEntities[20];
for(int i = 0 ; i < 20 ; i++ ){
ostringstream nameStream;
nameStream << "Sphere_" << i;
string name = nameStream.str();
Entity* sphere = new Entity(Ogre::Vector3(i*5, i*4.5, i*6), Ogre::Vector3(i*5, i*4.5, i*6), "sphere.mesh", name);
sphere->init(mSceneMgr, mPhysics, gScene, PxVec3(-10.0f, 0, 0));
mEntities[i] = sphere;
}
I got Access violation. W/ the just-in-time debugger, it turned out that mActorwas null as well as mMaterial
EDIT:
This code does not work either:
mEntity = Entity(Ogre::Vector3(50.0f, 50.0f, 50.0f), Ogre::Vector3(50.0f, 40.5f, 60.0f), "sphere.mesh", "sphere");
mEntity.init(mSceneMgr, mPhysics, gScene, PxVec3(-10.0f, 0, 0));
1)
Entity* sphere = new Entity(Ogre::Vector3(i*5, i*4.5, i*6),
Ogre::Vector3(i*5, i*4.5, i*6),
"sphere.mesh",
"Sphere_"+i);
Look at the "Sphere_"+i
If the i is larger then length of ”Sphere_” you are passing pointer to some random memory. I assume that you wanted to create a string with i at the end.
Use sprintf or std::string for that.
2)
If you change the loop range from 20 to let's say 3 it will probably work. The problem is that your names will be:
Sphere_, phere_, here_
Because by doing "Sphere_"+i you are not adding integer to the string.
This is "equal" to:
char *string = "String";
string += 3;
3)
This code will generate string that you need:
std::ostringstream newStringStream;
newStringStream << "Sphere_" << i;
std::string newString = newStringStream.str();
Here is another issue:
PxGeometry* geometry = NULL;
if(mMesh == "sphere.mesh")
{
geometry = &PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
}
The problem with this is that you are assigning to geometry the address of a temporary value. Once that line of code is completed, that temporary is gone.
The probable fix for this is to do this:
PxGeometry geometry;
if(mMesh == "sphere.mesh")
{
geometry = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
}
//...
mActor = PxCreateDynamic(*physics, transform, geometry, *mMaterial, PxReal(.1));
Now geometry is no longer a pointer, and you're assigning geometry to the value returned, (not address-of the value returned).
I am reading the documentation here:
http://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/apireference/files/classPxSphereGeometry.html
So PxSphereGeometry(x) is a constructor call. So you need to assign the return value to a PxShpereGeometry, not a PxSphereGeometry*.
Edit: Your latest changes also do not have the desired effect:
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = &g;
}
The g is local to the if() block. You assign the address of this g to geometry. Then when that block exits, g is gone, and now you have geometry pointing to something that no longer exists.
The difference between your edited code and the answer I gave is that my answer assigns the return value to an existing object. So I created a duplicate of the return value. What your doing in the edited code is not creating a duplicate, but pointing to a local object, which as explained, won't exist after it leaves scope.
So if you were to write code that follows the pattern of your edited code, and have it be valid, the change would look like this:
PxGeometry geometry;
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = g;
}
However, this does extraneous work. The original answer is sufficient.
I tried the alternate way to create a rigid body and it worked!!!!
mActor = physics->createRigidDynamic(PxTransform(PxVec3(mPosition.x, mPosition.y, mPosition.z)));
PxShape* shape = mActor->createShape(PxSphereGeometry(mDimensions.x / 2), *mMaterial);
PxRigidBodyExt::updateMassAndInertia(*mActor, 0.4f);
I'm trying to make a texture handler so that I can load texture file names from a text file and then load the textures and store them into a vector and get them whenever I need to draw.
Problem is, i'm getting the C2259 error which breaks before it can compile and was wondering if anyone could help me out.
TextureManager.h
class TextureManager{
private:
std::vector<ID3D11ShaderResourceView> * textures;
public:
TextureManager();
~TextureManager();
void TMLoadTexture(ID3D11Device* d);
ID3D11ShaderResourceView * TMgetTexture(int index);
};
TextureManager.cpp - TMLoadTexture / TMGetTexture
void TextureManager::TMLoadTexture(ID3D11Device* d)
{
std::vector<std::string> files;
files = readFile("textures");
D3DX11_IMAGE_LOAD_INFO loadInfo;
ZeroMemory(&loadInfo, sizeof(D3DX11_IMAGE_LOAD_INFO));
loadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
loadInfo.Format = DXGI_FORMAT_BC1_UNORM;
for(int i = 0; i < files.size(); i++)
{
std::wstring stemp = std::wstring(files.at(i).begin(), files.at(i).end());
LPCWSTR sw = stemp.c_str();
ID3D11ShaderResourceView* temp;
D3DX11CreateShaderResourceViewFromFile(d, sw, &loadInfo, NULL, &temp, NULL);
textures->push_back(*temp);
delete temp;
}
}
ID3D11ShaderResourceView * TextureManager::TMgetTexture(int index)
{
return &textures->at(index);
}
Thanks :)
Since ID3D11ShaderResourceView is an interface, you must use a pointer to access these kind of objects. So:
std::vector<ID3D11ShaderResourceView*> * textures;
Btw, are you sure that you want to use a vector-pointer? I see no reason why a plain vector<...> wouldn't be sufficient.
Then when loading the texture, put the pointer in the vector:
textures.push_back(temp);
And don't delete the texture you just created.
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!