Chipmunk collisions not happening when moving sprite using actions - cocos2d-x

Really a basic question, but I am moving a sprite A with a physics body over another kind of sprites B having another physics body. I expect the collision callback oncontact to be called for these bodies. They have their respective category bitmasks set using setCategoryBitmask() and also respectively have each other's categories set using setContactTestBitmask().
The collision works as long as I do not move sprite A. I assume the problem is that I move sprite A using cocos2d actions, and I need to do something else. But using cocos2d actions for scripting things like these look so much simpler to me than anything else I can think of.
Move sprite A using physics calls. (looks like a lot of work, and it looks like it's hard to achieve exact scripting perfection)
Do my own collision detection in update() instead. (looks like a bunch of work too, especially if the sprites are rotated etc)
Is there any other shortcut? Or did I miss something else?

I ended up doing "Do my own collision detection in update() instead". It wasn't too problematic.
Something like this in update()...
const Rect RECT_A = spriteA->getBoundingBox();
for (auto spriteB : someparent->getChildren()) {
const Rect RECT_B = spriteB->getBoundingBox();
if (RECT_A.intersectsRect(RECT_B)) {
// Hit
}
}

Contaction detection should be fine while collion doesn't work.
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::createWithPhysics();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
scene->getPhysicsWorld()->setGravity(Vec2::ZERO);
// return the scene
return scene;
}
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
auto createSprite = [this](const Vec2& pos) {
auto sprite = Sprite::create();
auto bodyA = PhysicsBody::createCircle(20);
sprite->setPhysicsBody(bodyA);
sprite->setPosition(pos);
bodyA->setCollisionBitmask(0xff);
bodyA->setCategoryBitmask(0xff);
bodyA->setContactTestBitmask(0xff);
return sprite;
};
auto spriteA = createSprite(Vec2(300, 300));
auto moveBy = MoveBy::create(1.f, Vec2(200, 200));
spriteA->runAction(moveBy);
auto spriteB = createSprite(Vec2(350, 350));
addChild(spriteA);
addChild(spriteB);
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = [=](PhysicsContact& contact) {
auto a = contact.getShapeA()->getBody()->getNode();
auto b = contact.getShapeB()->getBody()->getNode();
assert((a == spriteA && b == spriteB) || (a == spriteB && b == spriteA));
log("It is working");
return false;
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
return true;
}

Related

How to give differents directions of bullets on one array?

im very new on javascript, i was doing a ship gaming that you have to kill some asteroids, and when you take some differents of "objects" on the screen we expand our number of bullets. Okey, going to the point i could get 3 bullets on the screen when you take diffirents objects, but now i want to give 2 of that 3 bullets of the array different directions. When i tried, i have the problem that i give the 3 bullets the same direction, i know why but im for at least 5hrs trying to fix this and i cant.
Im programming on Flash Builder 4.7 with different classes, i ll give the code of the array who is in the main, and the bullet class so as the hero class too.
Main Array
public function evUpdateBullet():void//here execute update of my class Bullets
{
var i:int;
for(i=myBullets.length-1; i >= 0; i--)
{
if(myBullets != null) //to be ?
{
if(myBullets[i].isDestroyed) //is destroyed Bullets?
{
myBullets[i] = null;
myBullets.splice(i, 1); //deleted elements.
}else
{
myBullets[i].evUpdate();
}
}
}
}
here i push the array and create the bullet, remember myBullets is the name of the array.
public function evShoot(posX:int, posY:int):void//here create the bullet and push in the array
{
attack1 = new Bullet;
attack1.Spawn(posX, posY);
myBullets.push(attack1);
}
here i show the Hero code, where i define the position of the bullet is going to spawn on the screen.
if (isPressing_Shoot && !isDestroyed)// Here execute the event shoot without power
{
Main.instace.evShoot(model.x, model.y);
isPressing_Shoot = false;
canShoot = false;
}
evDestroyed();
}
here is the code from Bullet class
first the spawn
public function Spawn(posX:int, posY:int):void
{
isDestroyed = false;//first parameter of my bullet
model = new MCbullet;
Main.layer1.addChild(model);//painting the hero in the stage
model.x = posX;//position in the stage wiht the hero
model.y = posY;
model.tigger.visible = false;
}
then the Update
public function evUpdate():void//here conect with update general
{
if (model != null)//to be?
{
model.y -= 12;//move of my bullet
//model.x -= 12;
if (model.y <= 0 )
{
evDestroyed();
}
}
}
in this update i set the movement of y, so i can shoot vertically, but.. when i try to add an x.move, i do for the all array, so i want to know how i can give different move, for differents bullets of the same array.
Iterate through the array elements. There are a few ways to do this, but the one I'm most accustomed to using would be the for loop. It looks like this:
// loop through myBullets array to update
// each x and y position dependent on unique
// property value _xSpeed and _ySpeed.
for (var i:int = 0; i < myBullets.length; i++)
{
myBullets[i].x += myBullets[i]._xSpeed;
myBullets[i].y += myBullets[i]._ySpeed;
}
Obviously, you will need the _xSpeed and _ySpeed properties of the array elements to be set to dynamic values. You would first need to give the bullet class these properties and then set their values when you instantiate the bullets. That might look something like this:
function makeBullet():void{
var b:Bullet = new Bullet();
b.x = hero.x;
b.y = hero.y;
b._xSpeed = hero._xSpeed; // or put here whatever makes sense for assigning the right value in your application
And in your bullet class constructor, before the function but inside the class brackets, add the property:
var _xSpeed:Number = 0;
var _ySpeed:Number = 0;
Basically this is allowing each bullet to hold it's own special property that is independent of any other instance of the class.
I hope that helps.

Collision Detection not working in cocos2dx v3 using Physics

I want to collide 2 sprites using Physics Engine.
But my contact listener is not responding. and no warning/error on log or runtime.
So here is what i did :
Declaration
1)cocos2d::PhysicsWorld* m_world;
2)void setPhyWorld(cocos2d::PhysicsWorld* world){m_world = world;}
3)bool onContactBegin(cocos2d::PhysicsContact& contact);
Implementation
For PhysicsWorld:
Scene ->
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::createWithPhysics();
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
auto layer = HelloWorld::create();
layer->setPhyWorld(scene->getPhysicsWorld());
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
PhysicsWorld->
void HelloWorld::createPhysicsWorld(){
auto body = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3);
auto edgeNode = Node::create();
edgeNode->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
edgeNode->setPhysicsBody(body);
this->addChild(edgeNode);
}
Creating Sprite->
void HelloWorld::createCar(){
car = Sprite::create("car.png");
auto body3 = PhysicsBody::createBox(car->getContentSize());
body3->setDynamic(false);
body3->setCategoryBitmask(2);
body3->setCollisionBitmask(2);
body3->setContactTestBitmask(true);
car->setPhysicsBody(body3);
// position the sprite on the center of the screen
car->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
// add the sprite as a child to this layer
this->addChild(car, 1);
car->setAnchorPoint(Vec2(0.5, 0));
car->setFlippedX(true);
}
void HelloWorld::createRocket(){
rocket = Sprite::create("rocket.png");
auto body2 = PhysicsBody::createBox(rocket->getContentSize());
body2->setDynamic(false);
body2->setCategoryBitmask(1);
body2->setCollisionBitmask(1);
body2->setContactTestBitmask(true);
rocket->setPhysicsBody(body2);
// position the sprite on the center of the screen
rocket->setPosition(Vec2(visibleSize.width/2 + origin.x,origin.y));
// add the sprite as a child to this layer
this->addChild(rocket, 1);
}
Collision->
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener,this);
ContactListner->
bool HelloWorld::onContactBegin(cocos2d::PhysicsContact& contact)
{
CCLOG("onContactBegin -------> ");
return true;
}
Well it is tricky thing that you miss in your code.one body should be setDynamic to true.
body->setDynamic(true);
cheers!
Well, it turned out that it was bit ugly mistake.
body3->setContactTestBitmask(true);
this method's argument is a bitmask not 'true'/'false' field, so your 'true' mask was treated as it was 1 while your bodies mask is 2. The solution is:
body3->setCategoryBitmask(1);
body3->setCollisionBitmask(3);
body3->setContactTestBitmask(1);
body2->setCategoryBitmask(3);
body2->setCollisionBitmask(1);
body2->setContactTestBitmask(1);
I Just Comment out body2->setDynamic(false); inside createRocket Method and setGravityEnable(false); and it works now.
void HelloWorld::createRocket(){
rocket = Sprite::create("rocket.png");
rocket->setPosition(Vec2(visibleSize.width/2 + origin.x,origin.y - 50));
rocket->setTag(3);
auto body2 = PhysicsBody::createBox(rocket->getContentSize());
//body2->setDynamic(false);
body2->setTag(3);
body2->setGravityEnable(false);
body2->setCollisionBitmask(3);
body2->setContactTestBitmask(true);
rocket->setPhysicsBody(body2);
this->addChild(rocket, 1);
}

AS3 tracking and using coordinates of a rotated object

How does one track and use the coordinates of an object that is rotated on initialization?
Let's say I have a sword that is put on the stage in Main init(); and rotated (adjusted) so that it would look ok together with the character perspective. In another class however, I am making the sword rotate some more on a keypress timer event so to create a 'swing' animation.
All this is done through flashdevelop. I only used CS6 to create the symbols. And as this 'swing' is happening, I want to add another symbol onto the tip of the sword which is a collision point object. It's being added to the stage when the swing starts and removed after every swing. I want this object to follow the very tip of the sword, yet it seems like I can only achieve that it follows the coordinates of the original sword object, as if I hadn't initially modified the rotation of the said sword. I tried to implement GlobalToLocal() and LocalToGlobal() methods, but I don't think I fully understand what is happening with that.
I hope I'm being clear enough of what I'm trying to do. Thank you. This is the relevant code in question. The code is as was before I tried the two mentioned methods and the issue currently is exactly as described before that. Do I want any of those methods or am I just doing something else wrong?
Main initialization:
sword = new Sword();
sword.x = 53;
sword.y = 90;
addChild(sword);
sword.rotationZ = -150;
sword.rotationY = 25;
sword.rotationX = -15;
Coll_Point = new coll_point();
The class that deals with the swing has a method like this:
private function SwingTime(event:Event):void
{
Main.Coll_Point.x = Main.sword.x + Main.sword.width;
Main.Coll_Point.y = Main.sword.y + Main.sword.height;
Main.MazeNr1.addChild(Main.Coll_Point);
if (Main.sword.rotationZ > -330)
Main.sword.rotationZ -= 20;
if (Main.sword.rotationX < 15)
Main.sword.rotationX += 10;
if ((Main.sword.rotationZ == -330) && (Main.sword.rotationX == 15))
{
SwingTimer.stop();
SwingBckTimer.start();
}
}
Edit:
A more holistic version of the code:
public class Main extends MovieClip
{
public static var from_point:Point = null;
public static var to_point:Point = new Point();
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
// Puts everything on the stage here.
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
PlayerInst = new Dorf();
PlayerInst.x = 45;
PlayerInst.y = 51;
addChild(PlayerInst);
sword = new Sword();
sword.x = 53;
sword.y = 90;
sword.rotationZ = -150;
sword.rotationY = 25;
sword.rotationX = -15;
from_point = new Point (Main.sword.width, Main.sword.height);
to_point = sword.localToGlobal(from_point);
addChild(sword);
swordBD = new BitmapData(32, 32, true, 0x0000000000);
swordBD.draw(sword);
Coll_Point = new coll_point();
Coll_PointBD = new BitmapData(2, 2, true, 0x0000000000);
Coll_PointBD.draw(Coll_Point);
}
}
This is how the Main looks like and literally every single object instantiation is added onto the stage this way. Including collision points, background, characters, gradient fills of line of sight radius, etc. And the relevant symbol class goes somewhat like this:
public class Creature extends MovieClip
{
protected var Swing:Boolean;
private var SwingTimer:Timer = new Timer (5, 0);
private var SwingBckTimer:Timer = new Timer (150, 1);
// Constructor.
public function Creature()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
// Initializer.
private function init(event:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
SwingTimer.addEventListener(TimerEvent.TIMER, SwingTime);
SwingBckTimer.addEventListener(TimerEvent.TIMER, SwingBack);
}
private function SwingAction():void
{
if (Swing == true)
{
SwingTimer.start();
}
}
private function SwingTime(event:Event):void
{
Main.Coll_Point.x = Main.sword.localToGlobal(Main.from_point).x;
Main.Coll_Point.y = Main.sword.localToGlobal(Main.from_point).y;
Main.sword.addChild(Main.Coll_Point);
trace(Main.Coll_Point.x);
trace(Main.Coll_Point.y);
if (Main.sword.rotationZ > -330)
Main.sword.rotationZ -= 20;
if (Main.sword.rotationX < 15)
Main.sword.rotationX += 10;
if ((Main.sword.rotationZ == -330) && (Main.sword.rotationX == 15))
{
SwingTimer.stop();
SwingBckTimer.start();
}
}
private function SwingBack(event:Event):void
{
Main.sword.rotationZ = -150;
Main.sword.rotationX = -15;
//Main.MazeNr1.removeChild(Main.Coll_Point);
}
There is also a rather long update(); function that animates and moves every single object that needs moving.
I think your problem might be in
Main.Coll_Point.x = Main.sword.x + Main.sword.width;
Main.Coll_Point.y = Main.sword.y + Main.sword.height;
Coll_Point expects global coordinates.
The parts + Main.sword.width and + Main.sword.height only work as expected if the sword is not rotated so that height is aligned with the y-axis and width with the x-axis.
You should use localToGlobal() on the position that is local to Main.sword (
Main.sword.width, Main.sword.height) to get the global position that represents the swords rotated tip before you add it as a child.
There are two ways you can approach this (you seem to have somewhat combined both). You can either
Add the Coll_Point as a child to something above the sword in hierarchy (Stage, MazeNr1, ...) and update the position manually every timer callback. You would have to recalculate the position everytime, so take the localToGlobal() from init to your timer function. It won't update if it doesn't get called.
For that you should have this kind of code in the timer callback:
var local:Point = new Point(Main.sword.width, Main.sword.height);
var global:Point = Main.sword.localToGlobal(local);
Main.Coll_Point.x = global.x;
Main.Coll_Point.y = global.y;
Add the point as a child to the sword. This might be a better approach as then the position will be updated automatically. What I did not remember before was that you then give the coordinates in "local" form, so do not use localToGlobal()
Run this once where you create the Collision_Point:
Coll_Point.x = <your x offset>;
Coll_Point.y = <your y offset>;
Main.sword.attachChild(Coll_Point);
Instead of sword height and width you might want to try something like -height and width/2.
Here is a quick (and not the prettiest) picture to demonstrate the problem. Local space is rotated with the object:
The only thing I can imagine to help you with this problem is to have your collision object have the same registration point as the sword. What I mean is that the orientation points should match, and the collision graphic should be moved inside of the sprite so that it matches the position of the top of the sword.
This way you can put the collision object at the very same location of the sword and apply the very same rotation. This way it will move along with the top of the sword and still have hitTest working properly.
I cannot imagine any other way to figure this out as any code will get bounds and positions. But the real thing that matters is the registration point and the top of the sword, which is a graphic thing and cannot be dealt with coding.
I hope you can imagine what I mean - if now, just say and I will provide an image for you :)

Requesting value of property of a null object

I m currently converting my old game project which is in AS2 into AS3 by myself. And there was a problem. In the AS2 version of my game I used to check for a movieclip's sub movieclip's property and use it for some calculations, using
if (mc1.mc2.prop == undefined){
//do something
}
and during somepoint of the game the mc1 or mc2 is removed.
but in AS3 this no longer works because I cannot access the prop after mc1 or mc2 is removed.
Anyhelp? thanks.
It's hard to give a more concise answer without knowing how your game actually works, but this function will allow you to check if a hierarchical value exists on an object:
function hasProp(target:Object, prop:String):Boolean
{
var tests:Array = prop.split('.');
var test:* = target;
for each(var p:String in tests)
{
if(test.hasOwnProperty(p))
{
test = test[p];
}
else return false;
}
return true;
}
Used like:
if( hasProp(mc1, "mc2.prop") )
{
// Property exists.
}

What code in as-3 to use to allow seekbar to be moved by user?

I'm creating a simple music player which will play just one song. Stage timeline has got just one frame. There is main graphic for player which is movieClip_1 (it has 4 frames in it's own timeline) and that works ok. I've got button (button_2) which starts and pauses the song and the movieClip_1 (works ok). And i also have got a graphic (it's called tube - i have changed it to movie clip, it has got one frame inside it's timeline) as a seekbar component which I just want it to move correspondingly to channel.position of this song on the x axis which it does but gives me triple error of:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
Debug points at this line:
tube.x = (chan.position/song.length) * 83;
I would really appreciate a tip regarding error and also what method to use in order for user to be able to navigate (with mouse) through song by moving tube on x axis and going to different part of song. I read about .startDrag and .stopDrag would that be good idea with my present code?
My code so far:
movieClip_1.stop();
var itsstoped:Boolean;
var youpausedit:Boolean;
var counter:Timer = new Timer(100);
var chan:SoundChannel;
var song:Sound = new Sound();
song.load(new URLRequest("wmc2.mp3"));
song.addEventListener(Event.COMPLETE, soundloaded);
function soundloaded(event:Event)
{
trace("Song has been loaded.");
}
counter.addEventListener(TimerEvent.TIMER,countcounter);
itsstoped = true;
youpausedit = false;
button_2.addEventListener(MouseEvent.CLICK, presser);
function presser(event:MouseEvent):void
{
if(itsstoped == true && youpausedit == true)
{
movieClip_1.gotoAndPlay(1);
chan = song.play(chan.position);
itsstoped = false;
}
else if(itsstoped == false)
{
movieClip_1.gotoAndStop(1);
chan.stop();
itsstoped = true;
youpausedit = true;
}
else if(itsstoped == true && youpausedit == false)
{
movieClip_1.gotoAndPlay(1);
chan = song.play();
counter.start();
itsstoped = false;
}
}
function countcounter(e:TimerEvent):void
{
trace(chan.position/song.length);
var percentage:uint = 100 * (chan.position / song.length);
trace(percentage);
if(percentage == 100)
{
movieClip_1.gotoAndStop(1);
itsstoped = true;
youpausedit = false;
counter.stop();
}
}
tube.buttonMode = true;
tube.useHandCursor = true;
addEventListener(Event.ENTER_FRAME, movewhenplay);
function movewhenplay(e:Event):void
{
tube.x = (chan.position/song.length) * 83;
}
Regarding the error, you're accessing an object that has yet to be instantiated, i.e. there is no data to fetch as it does not exist.
As for the searchbar, I'm guessing you're using some kind of mask for the search bar, as you're using its x position to display the song position. I would suggest you instead use its width parameter, and set the center point to the far left.
To be able to then search through the song, you can add a copy of this searchbar, place it on top of the first one. Set its alpha to 0, to make it invisible, but still clickable. Add mouseEvent listeners to that, and when the user clicks it, you can set the song to play from the position which you can calculate using mouseX relevant to the invisible search bar.
I.e. if you want the clicked position in percents
percent = (mouseX - invisiSearchBar.x) / invisiSearchbar.width