Vectors & Iterators - Velocity Damping Code

Vectors & Iterators - Velocity Damping Code
0.0 0

#1

In my Header file I have this

        `std::vector<cocos2d::Sprite *> objects;`

In my update function I have this

	for (auto object : this->objects)
	{
		CCLOG("X:%f   Y:%f", object->getPosition().x, object->getPosition().y);
	}

However it breaks at CCLOG. Null Pointer

This is the tutorial I was following

http://dev.bunnyhero.org/2014/01/cocos2d-x-30-beta-the-new-vector-class/

Whats wrong with my code anyone ?

I want to cycle through all objects during the update function and alter there Velocity ! For now just accessing them all all in a CCLOG will suffice

Thanks


#2

Hello @GaddMaster,

Make sure you have your sprites added to the scene already if not then use cocos2d::Vector<Sprite*> instead of std::vector<Sprite*>.
cocos2d::Vector<t> is a container of Cocos2d-X which accepts objects of type Ref and set the flag for autorelease if not done already. Remember Sprites and Nodes don’t render if you don’t add them to your Scene

Also in your update do confirm you are not trying to iterate over a empty container this->objects and it actually has some objects in it .


#3

@Lazy_Gamer Should that be a problem? Wouldn’t the for loop just exit if the container is empty? Most probably his sprites were released/not retained at all and turned null at some time.

@GaddMaster Just as @Lazy_Gamer said, after adding your objects to the objects vector, make sure you call retain on them, or add them to the scene graph, or use cocos2d::Vector which automatically calls retain and release when you add or remove objects to or from the container.


#4

Vector is better than vector (lowercase) for cocos2d-x objects (like sprite), but that’s not the origin of the problem.
You simply should use . (dot) instead of ->
objects is not a pointer, because it lacks *

So change declaration to:
std::vector<cocos2d::Sprite *> *objects;
And then you have to use new like this:
objects = new std::vectorcocos2d::Sprite();
or (recommended) just use dot:
for(auto object : objects){ //or this.objects

}


#5

He is using -> for this: “this->objects” and it’s correct since this is a pointer. And in the CCLOG he is using -> for an object returned from the vector, and that’s also correct since auto object actually translates to Sprite* object, also a pointer.


#6

Ah well yeah, sorry about that. I was a bit confused for a second :smiley:
Well I’ve created a simple example and both declaration and loop is fine.
The reason it breaks is probably memory issue. If sprites inside “objects” vector aren’t added to the scene they’ll be released from memory. So you have to:
a) add them to scene
b) manually increase reference counter (remember to release them later)
c) use Vector class, which increases reference counter when object is on the list


#7

Well it works now guys. memory issue. I am actually trying to replicate a velocity damping functionality as we dont seem to have a velocity damping function, only a angular velocity function. Here I’ll share my code in case anyone wants to use it and change header to velocity damping code

HEADER FILE

	std::vector<cocos2d::Sprite *> objects;

INIT FUNCTION

	Circle* circleObject1 = new Circle();
	auto circle1 = circleObject1->getSprite();
	circle1->setPosition(-2850 / TIscale, 1000 / TIscale);
	this->objects.push_back(circle1);
	this->addChild(circle1);

	Circle* circleObject2 = new Circle();
	auto circle2 = circleObject2->getSprite();
	circle2->setPosition(-2825 / TIscale, 1000 / TIscale);
	this->objects.push_back(circle2);
	this->addChild(circle2);

	Circle* circleObject3 = new Circle();
	auto circle3 = circleObject3->getSprite();
	circle2->setPosition(-2860 / TIscale, 880 / TIscale);
	this->objects.push_back(circle3);
	this->addChild(circle3);

	Circle* circleObject4 = new Circle();
	auto circle4 = circleObject4->getSprite();
	circle4->setPosition(-2860 / TIscale, 900 / TIscale);
	this->objects.push_back(circle4);
	this->addChild(circle4);

	Circle* circleObject5 = new Circle();
	auto circle5 = circleObject5->getSprite();
	circle5->setPosition(-2860 / TIscale, 920 / TIscale);
	this->objects.push_back(circle5);
	this->addChild(circle5);

	Circle* circleObject6 = new Circle();
	auto circle6 = circleObject6->getSprite();
	circle6->setPosition(-2860 / TIscale, 940 / TIscale);
	this->objects.push_back(circle6);
	this->addChild(circle6);

UPDATE FUNCTION

for (auto object : this->objects)
{
	if (object->getPhysicsBody()->getVelocity().x > 0)
		object->getPhysicsBody()->setVelocity(Vec2(object->getPhysicsBody()->getVelocity().x - 1, object->getPhysicsBody()->getVelocity().y));
	if (object->getPhysicsBody()->getVelocity().x < 0)
		object->getPhysicsBody()->setVelocity(Vec2(object->getPhysicsBody()->getVelocity().x + 1, object->getPhysicsBody()->getVelocity().y));
	if (object->getPhysicsBody()->getVelocity().y > 0)
		object->getPhysicsBody()->setVelocity(Vec2(object->getPhysicsBody()->getVelocity().x, object->getPhysicsBody()->getVelocity().y - 1));
	if (object->getPhysicsBody()->getVelocity().y < 0)
		object->getPhysicsBody()->setVelocity(Vec2(object->getPhysicsBody()->getVelocity().x, object->getPhysicsBody()->getVelocity().y + 1));

	circleVelocity->setString("Circle Velocity X:" + std::to_string(object->getPhysicsBody()->getVelocity().x) + "  Y:" + std::to_string(object->getPhysicsBody()->getVelocity().y));
}

Thanks very much guys. Problem was not adding them to scene in init function as described by most of you


#8

What is “Circle”? Is it extended Node/Sprite? Avoid using “new” keyword directly. Instead use CREATE_FUNC pattern to enable autorelease.


#9

No idea what ya mean about not using new and using CREATE_FUNC. Circle is just a circle object is all

#ifndef CIRCLE_H
#define CIRCLE_H

#include "cocos2d.h"

class Circle
{
	public:

		Circle();
		~Circle();

		cocos2d::Sprite* getSprite();
		cocos2d::PhysicsBody* getPhysicsBody();

	private:
		cocos2d::Sprite* sprite;
		cocos2d::PhysicsBody* physicsBody;
		float TIscale = 1.71;
};

#endif

#include "Circle.h"

USING_NS_CC;

Circle::Circle()
{
	sprite = Sprite::create("Obstacles/circle.png");
	physicsBody = PhysicsBody::createCircle(20 / TIscale, PhysicsMaterial(0.1, 0, 0));
	sprite->setPhysicsBody(physicsBody);
	physicsBody->setCollisionBitmask(4);
	physicsBody->setContactTestBitmask(true);
	physicsBody->setVelocityLimit(30);
	physicsBody->setAngularDamping(10);
}

Circle::~Circle()
{

}

cocos2d::Sprite* Circle::getSprite()
{
	return sprite;
}

cocos2d::PhysicsBody* Circle::getPhysicsBody()
{
	return physicsBody;
}

We have square and triangle of which they have about 10 points for the physics body, the reason i made class for them.
The reason I adding them to a vector is because I want to create a Velocity Damping effect in the update by reducing the velocity x and velocity y towards zero each update. Like so

UPDATE FUNCTION

	for (auto object : this->objects)
	{
		if (object->getPhysicsBody()->getVelocity().x > 0)
			object->getPhysicsBody()->setVelocity(Vec2(object->getPhysicsBody()->getVelocity().x - 1, object->getPhysicsBody()->getVelocity().y));
		if (object->getPhysicsBody()->getVelocity().x < 0)
			object->getPhysicsBody()->setVelocity(Vec2(object->getPhysicsBody()->getVelocity().x + 1, object->getPhysicsBody()->getVelocity().y));
		if (object->getPhysicsBody()->getVelocity().y > 0)
			object->getPhysicsBody()->setVelocity(Vec2(object->getPhysicsBody()->getVelocity().x, object->getPhysicsBody()->getVelocity().y - 1));
		if (object->getPhysicsBody()->getVelocity().y < 0)
			object->getPhysicsBody()->setVelocity(Vec2(object->getPhysicsBody()->getVelocity().x, object->getPhysicsBody()->getVelocity().y + 1));
	}

#10

I think you should extend cocos2d::Ref in your Circle class and use CREATE_FUNC. “sprite” inside Circle will be autoreleased, but not Circle itself. It’s not a big deal, but just for convenience you should do this. I never use “new” keyword without using CC_SAFE_DELETE later. But that’s for another types of stuff like vector<Node*>* for example (when I need a pointer and there’re cases where I really need it).


#11

Yup that will be a problem. If Sprites/Nodes or any Ref object are not retained they will be autoreleased() by engine as soon as execution returns from method. So in this case container will be left with nullptr and as @GaddMaster is using enhanced for loop using auto keyword for traversal in that case auto will keep nullptr as object and when next line executes i,e object->getPosition().x it will crash due to nullptr don’t have a method getPosition() easy fix is check if object is nullptr and exit early.[quote=“piotrros, post:4, topic:35175”]
Vector is better than vector (lowercase) for cocos2d-x objects (like sprite), but that’s not the origin of the problem.
[/quote]

In my personal opinion cocos2d::Vector should only be used if we have a object pool implementation (I only use them that way :wink: ), else i always adds my Nodes into the Layer or Scene as they will be autoreleased() when their parent is destroyed, saves me from memory leaks :smile:.[quote=“GaddMaster, post:7, topic:35175”]
Well it works now guys. memory issue.
[/quote]

Great going forward :slight_smile:[quote=“piotrros, post:10, topic:35175”]
I think you should extend cocos2d::Ref
[/quote]

If i were @GaddMaster

I would just extend Sprite for my circle class, it will give me physicsBody as well as Sprite rendering components, and would use setTag() to identify them. That way i would have my circle carry any custom data Structure with it along with velocity and damping code contained in itself. A complete independent object which can damp itself according to environment as and when needed.

Hope this will help :slight_smile:


#12

Yeah but when I said empty container I was referring to a container that has vector.size() = 0 or vector.empty() = true, and not a container of null pointers(that’s not actually empty, it contains pointers to null). So in this case I think the for loop will work(I mean it won’t crash).

And why don’t you use normal vectors in an object pool? Just because of the auto retain/release?


#13

Yes it should and it will return out of the loop. But this is not the case with OP, his container was pointing to nullptr left off by Sprite init() method as its initialized with autorelease() flag internally as he exited the init() or whatever method he has for initializing circle objects, autorelease will check if Sprite object is retained or not as soon as execution return from init() of Layer or Scene? if not then it will release it making its pointer point to nullptr, leaving OP’s vector filled with garbage. Hence crash due to EXC_BAD_ ACCESS.

I do use std::vector<t*> in my Object Pools but only if i know my objects are already being retained somewhere or i inherited them in and not using autorelease() and willing to do manual memory management. Best use case for using cocos2d::Vector<t*> for me is when i download textures from server and populating my UI only and when required on a later time.


#14

What you mean by this ? Does that T* have a special meaning and do you mean like we may have these objects already stored in another vector, and oncve we require to stroe them in a second vector we should use a different type of vector T* ?


#15

Hello @GaddMaster,

i usually prefer std::vector<t*> of unordered_map(from STL) to create my object pools(because of quick read time), where t* denotes template <class myType*>, nothing fancy, so t* is just a reference of Type myClass

No we can’t use different type until and unless myClass is a child class of mySuperClass and we are doing some dynamic casting between the two, while storing to different vector.

checkout this simple pool implementation using std::list<Resource*> resources; where resources are custom class Type. Hope this example clears a bit of your doubt. https://gist.github.com/pazdera/1124832


#16

O Yes I thought so . . . . . :slight_smile: