CompositeSprite Collision
by Jesse Allen · in Torque 2D Beginner · 11/19/2013 (1:21 pm) · 14 replies
Greetings all! I have been trying to figure out a way to use CompositeSprites to build a huge grid of square tiles similar to what you would see in Terraria. I have been able to render CompositeSprites to cover all of X or all of Y, or even whichever quadrant of the screen I wanted them in. I was thinking I may be ready to start adding collision shapes and mess with those, and then realized there is no information available about adding collision shapes to sprites within a composite sprite. How does one achieve this?
I browsed through Torsion and found this:
createPolygonBoxCollisionShape(width,height,[localCentroidX,localCentroidY],[angle])
However, when I try to use this as a method it always throws a parse error. I have tried:
I took the arguments to mean the following:
width: width of the collision box being created
height: height of the collision box being created
localCentroidX,Y: center point of the box being created (would align with Sprites 'local' coordinates within the CompositeSprite)
angle:Angle of the collision box being created
CompositeSprites are supposed to be the 'new' way to create tilemaps, although thus far I have been unable to use them for anything other than rendering 'programmer art'. How can I use collision inside CompositeSprites on a per Sprite basis?!
I browsed through Torsion and found this:
createPolygonBoxCollisionShape(width,height,[localCentroidX,localCentroidY],[angle])
However, when I try to use this as a method it always throws a parse error. I have tried:
%composite.createPolygonBoxCollisionShape(4,4[0,-6],[0]);That didn't work, so I tried:
%composite.createPolygonBoxCollisionShape(4,4,0,-6,0);...still no dice. What am I missing here? Is this indeed a usable method for use within my Composite Sprite?
I took the arguments to mean the following:
width: width of the collision box being created
height: height of the collision box being created
localCentroidX,Y: center point of the box being created (would align with Sprites 'local' coordinates within the CompositeSprite)
angle:Angle of the collision box being created
CompositeSprites are supposed to be the 'new' way to create tilemaps, although thus far I have been unable to use them for anything other than rendering 'programmer art'. How can I use collision inside CompositeSprites on a per Sprite basis?!
About the author
Skilled Artist and Musician. Intermediate Torque Developer.
#2
I had finally dropped an animated sprite into the scene to try out some different options with the terrain. When I realized I wasn't able to get the composite sprite to collide with it, it was funny trying to keep my guy above ground by jumping haha. Now I actually have something I can build on. You've been helpful as usual, cheers!
11/19/2013 (2:31 pm)
Mike: Man, you have no idea how glad I am to finally have the missing link here! Mike to the rescue! I couldn't get my head around the collisions being separate (SceneObject level). This solves it, Mike, you are a lifesaver!I had finally dropped an animated sprite into the scene to try out some different options with the terrain. When I realized I wasn't able to get the composite sprite to collide with it, it was funny trying to keep my guy above ground by jumping haha. Now I actually have something I can build on. You've been helpful as usual, cheers!
#3
Also, while I was adjusting the above code to fit my scene I realized that this isn't going to work regardless. You have:
Correct me if I'm wrong but isn't that only creating a single collision box, as opposed to a collision box for every tile within the composite? If this does actually create a collision box for each tile, how would you keep up with them, add collision groups, and add/remove them individually later?
In any event, I wasn't able to even render a compositeSprite at all with the above code. Since you set the position to -10, -10 I also went offscreen with my animated Sprite character (camera locked on player) to look and nothing. Since 10 is a pretty big block I thought it may have been placed farther away. If my math is right, though, I shouldn't have had to look far.
Anyways here is what I have that does render a compositeSprite for me, with the added collision command (renders up to -6 Y, keeping top edge of tiles closer to bottom of screen):
You can see here that I tried to add the setCollisionGroups(10 15); method but I'm still not getting any collision. No parse errors though, so that's a start. My player is also set to collide with Groups 10 and 15.
11/19/2013 (4:36 pm)
Perhaps I spoke too soon...I copy/pasted your above sprite and didn't get anything on screen at all. I changed the image to Blocks etc. etc. Also, while I was adjusting the above code to fit my scene I realized that this isn't going to work regardless. You have:
%position = %map.getSpriteLocalPosition(); %map.createPolygonBoxCollisionShape(10, 10, %position);
Correct me if I'm wrong but isn't that only creating a single collision box, as opposed to a collision box for every tile within the composite? If this does actually create a collision box for each tile, how would you keep up with them, add collision groups, and add/remove them individually later?
In any event, I wasn't able to even render a compositeSprite at all with the above code. Since you set the position to -10, -10 I also went offscreen with my animated Sprite character (camera locked on player) to look and nothing. Since 10 is a pretty big block I thought it may have been placed farther away. If my math is right, though, I shouldn't have had to look far.
Anyways here is what I have that does render a compositeSprite for me, with the added collision command (renders up to -6 Y, keeping top edge of tiles closer to bottom of screen):
function World::createCompositeGround( %this )
{
worldScene.setLayerSortMode( 0, "z" );
%composite = new CompositeSprite();
%composite.setDefaultSpriteStride( 4 );
%composite.setDefaultSpriteSize( 4 );
%composite.SetBatchLayout( "rect" );
%composite.SetBatchSortMode( "z" );
%composite.SetBatchIsolated( World.RenderIsolated );
%composite.GravityScale = 0;
%xrange = 12;
%yrange = 9;
for ( %y = -%yrange; %y <= -6; %y++ )
{
for ( %x = -%xrange; %x <= %xrange; %x++ )
{
%composite.addSprite( %x SPC %y );
%composite.setSpriteImage( "Assets:Blocks", getRandom(0,55) );
%position = %composite.getSpriteLocalPosition();
%composite.createPolygonBoxCollisionShape(4, 4, %position);
%composite.setCollisionGroups ( 10, 15 );
}
}
// Add to the scene.
worldScene.add( %composite );
// Set the composite sprite toy.
World.CompositeSprite = %composite;
}You can see here that I tried to add the setCollisionGroups(10 15); method but I'm still not getting any collision. No parse errors though, so that's a start. My player is also set to collide with Groups 10 and 15.
#4
1. What is the size of your camera view? (Sandbox uses 100 x 75 m)
2. Is the camera positioned at the origin (0, 0)?
3. Is gravity turned on or do you have some other constant force being applied in your scene?
Keep in mind the default body type for all physics objects is "dynamic". So if you do have gravity turned on, my CompositeSprite is simply falling down a bottomless pit. Change the body type to static to fix that.
11/19/2013 (10:21 pm)
Well, my example was just a simple proof of concept that works within the context of the Sandbox. It does create 9 collision shapes that match the size and position of each tile in the composite - I can see this with the debug rendering from the Sandbox. Copy/Pasting to your module and world setup doesn't guarantee success. Some questions that might help in solving the problem:1. What is the size of your camera view? (Sandbox uses 100 x 75 m)
2. Is the camera positioned at the origin (0, 0)?
3. Is gravity turned on or do you have some other constant force being applied in your scene?
Keep in mind the default body type for all physics objects is "dynamic". So if you do have gravity turned on, my CompositeSprite is simply falling down a bottomless pit. Change the body type to static to fix that.
#5
1.Camera is 100x75.
2.Postion is 0,0
3.Gravity is on. I tried with the object set to static.
I am totally lost at this point, I have followed all of the physics documentation precisely. I even added every single field involving the physics manually to the player sprite and the blocks to be sure something wasn't defaulting to the wrong setting. I even used the setCollisionAgainst() method to try and make it work directly. I'll continue to tinker with it, though at this point I'm at a standstill. Also to note is that within the Sandbox the main.cs from the CompositeSpriteToy controls some stuff like the number of blocks. Perhaps something there is causing a problem?
At this point I'm almost ready to just e-mail you the whole project so someone who knows what they are doing can figure out the problem. Short of recreating the entire project in the Sandbox, I don't know what else to do at this point as all of the relevant fields are in place and should be working. No parse errors. Sigh.
11/20/2013 (11:48 am)
Color me confused. Yes I know that it won't just work without changing it to fit my scene. I changed the valid fields there. I also tried to use your example in the Sandbox and can't get anything to show up. I just replaced the default rectLayout.cs with the example. Nothing; no sprite, no collision boxes. Weird. 1.Camera is 100x75.
2.Postion is 0,0
3.Gravity is on. I tried with the object set to static.
I am totally lost at this point, I have followed all of the physics documentation precisely. I even added every single field involving the physics manually to the player sprite and the blocks to be sure something wasn't defaulting to the wrong setting. I even used the setCollisionAgainst() method to try and make it work directly. I'll continue to tinker with it, though at this point I'm at a standstill. Also to note is that within the Sandbox the main.cs from the CompositeSpriteToy controls some stuff like the number of blocks. Perhaps something there is causing a problem?
At this point I'm almost ready to just e-mail you the whole project so someone who knows what they are doing can figure out the problem. Short of recreating the entire project in the Sandbox, I don't know what else to do at this point as all of the relevant fields are in place and should be working. No parse errors. Sigh.
#6
I wanted to work on the "proof of concept" a bit further to help you out but I am struggling with collision groups, for some reason they are not behaving as expected. Going to have to do some digging.
11/20/2013 (12:32 pm)
Do you have a way to zip up your module(s) and send them to me? Might be the easiest way to figure out what is wrong.I wanted to work on the "proof of concept" a bit further to help you out but I am struggling with collision groups, for some reason they are not behaving as expected. Going to have to do some digging.
#7
As a great big plus though, I did finally get your compositeSprite to show up! I have the 9 blocks on screen now, which is very cool because I like the way that you used the math for placement. I hadn't tried to turn on collision yet, but they are there! Anyways, I'll zip this thing up and send it to you shortly. Thanks Mike, people like you make this community a pleasure to be a part of.
edit: Sent, thanks again!
11/20/2013 (12:49 pm)
Sure thing, Mike. I'd be more than happy to send you the project so you can take a look. I've been pulling my hair out for the past day or so trying to get collision to work within compositeSprites. It's my birthday today, and I'd rather not struggle with this. Obviously I have something setup incorrectly. As a great big plus though, I did finally get your compositeSprite to show up! I have the 9 blocks on screen now, which is very cool because I like the way that you used the math for placement. I hadn't tried to turn on collision yet, but they are there! Anyways, I'll zip this thing up and send it to you shortly. Thanks Mike, people like you make this community a pleasure to be a part of.
edit: Sent, thanks again!
#8
Had a quick look at your scripts. The CompositeSprite is setup ok, all I had to do was comment out this line:
You had turned your player sprite into a sensor which means it can receive collision callbacks but it does not cause a collision response (i.e. colliding with the composite and not falling through the floor).
With that line gone, the player can then walk along the composite as expected.
11/20/2013 (2:46 pm)
Happy Birthday!Had a quick look at your scripts. The CompositeSprite is setup ok, all I had to do was comment out this line:
World.Player.setCollisionShapeIsSensor(0, true);
You had turned your player sprite into a sensor which means it can receive collision callbacks but it does not cause a collision response (i.e. colliding with the composite and not falling through the floor).
With that line gone, the player can then walk along the composite as expected.
#9
Man, thanks! That's honestly one of the best presents I could have. Now I'm going to continue study about randomizing the terrain, your help has been invaluable!
11/20/2013 (5:46 pm)
/facepalmMan, thanks! That's honestly one of the best presents I could have. Now I'm going to continue study about randomizing the terrain, your help has been invaluable!
#10
I've tried several things to stop this from happening, but ultimately I feel this may be a bug with how the CompositeSprite is creating and handling collision boxes. Would anyone have any ideas or solutions? My entire project depends on the collision boxes working correctly, even if generated with the CompositeSprite.
11/25/2013 (12:27 pm)
I have found another problem with CompositeSprite collision boxes. My animated sprite character is able to walk atop the composite, but occasionally he gets 'hung' while crossing to the next square. This is very random:- The character may walk across the top of a rectangular CompositeSprite just fine, only to turn around and then one of the squares he just walked across 'blocks' him. A quick jump can get the character over the corner where the collision boxes meet, but the problem persists.
The character may walk in one direction fine, while traveling the opposite direction it seems if the problem occurs every other collision box will cause an invisible 'blockage' to occur.
I've tried several things to stop this from happening, but ultimately I feel this may be a bug with how the CompositeSprite is creating and handling collision boxes. Would anyone have any ideas or solutions? My entire project depends on the collision boxes working correctly, even if generated with the CompositeSprite.
#11
Imagine a rectangular CompositeSprite that is 5x5 squares. If the character is standing on top of the whole CompositeSprite object, all is well and he can travel left and right with no issues.
If the character bumps the side of the whole CompositeSprite body, when he then tries to walk across the top he will bump into invisible barriers over every other block. If the character bumped into the right side of the entire CompositeSprite body (moving left), then when he moves on the top he will run into barriers also while moving left (moving right is fine in this case).
It seems as though when a character hits the entire CompositeSprite body from one direction, all of the collision boxes within are being flagged to respond to that single direction. But if the character then collides with the CompositeSprite body from another direction, all of the collision boxes within aren't updated correctly.
All signs point to this being a bug in how the CompositeSprite handles all collision boxes created with it. Since the CompositeSprite treats every box within as being contacted at the same time, it should make all collision boxes react to the same side at the same time. This is not the case, as every other 'inner' collsion box is failing to update when the CompositeSprite is collided against from a particular side.
Alternatively, if a collision box is created not within the bounds of a CompositeSprite it will respond to all sides equally as expected. Basically this means that, while CompositeSprites would have been the best approach for building my level, I won't be able to use CompositeSprites after all. This puts me building every single tile location and collision box individually, which makes me wonder why CompositeSprites support being able to make collision boxes 'per sprite' if the collision boxes don't all work together.
11/25/2013 (2:50 pm)
I managed to isolate the cause of the collision problem. Imagine a rectangular CompositeSprite that is 5x5 squares. If the character is standing on top of the whole CompositeSprite object, all is well and he can travel left and right with no issues.
If the character bumps the side of the whole CompositeSprite body, when he then tries to walk across the top he will bump into invisible barriers over every other block. If the character bumped into the right side of the entire CompositeSprite body (moving left), then when he moves on the top he will run into barriers also while moving left (moving right is fine in this case).
It seems as though when a character hits the entire CompositeSprite body from one direction, all of the collision boxes within are being flagged to respond to that single direction. But if the character then collides with the CompositeSprite body from another direction, all of the collision boxes within aren't updated correctly.
All signs point to this being a bug in how the CompositeSprite handles all collision boxes created with it. Since the CompositeSprite treats every box within as being contacted at the same time, it should make all collision boxes react to the same side at the same time. This is not the case, as every other 'inner' collsion box is failing to update when the CompositeSprite is collided against from a particular side.
Alternatively, if a collision box is created not within the bounds of a CompositeSprite it will respond to all sides equally as expected. Basically this means that, while CompositeSprites would have been the best approach for building my level, I won't be able to use CompositeSprites after all. This puts me building every single tile location and collision box individually, which makes me wonder why CompositeSprites support being able to make collision boxes 'per sprite' if the collision boxes don't all work together.
#12
When all of this comes to fruition, this problem may not even be an issue because of the different way I'll be handling the CompositeSprite. Time will tell at this point, but I haven't given up completely on the idea of using the CompositeSprite.
11/26/2013 (1:01 pm)
I wanted to post a quick update about all this collision business. Currently I am in the process of restructuring the process of adding/removing sprites within my project. When all of this comes to fruition, this problem may not even be an issue because of the different way I'll be handling the CompositeSprite. Time will tell at this point, but I haven't given up completely on the idea of using the CompositeSprite.
#13

11/26/2013 (9:56 pm)
There is a bug of sort in the way the composite sprite OOBB/AABB are calculated in relation to it's collision shape that might be affecting you here. I am doing a bit fancier layering with CompositeSprite, The following diagram show the gist of it, a SimComposite object in this case contain a bunch of SimCompositeLayer each containing a CompositeSprite to be layered on this object, the SimComposite also contain 2 other structure each associated with another CompositeSprite and a QuadTree, one of those two object contain collision data for this SimComposite object, it's AABB/OOBB, which is in turn used for any picking, are never correct, they are only computed from the Sprite object of the CompositeSprite with no regard for the collision shapes, I had to compute those myself, in my case i used the OOBB of the QuadTree used to build collision data to override those comming from the Sprite objects. You might not need to go through all this trouble for static object, collision sprite inactivation work pretty well with a huge number of object on the graph as long as there is not a big number of them clumped together as-is, just be careful not to ever overlay groups of bounded collisions shape or it will kill performance, in my case everything is dynamic and special care is needed to allow reasonable performance with complicated moving objects and high number of sprites/blocks, hence the QuadTree structure used to compose collision shapes for all layer of the SimComposite together.
#14
You seem to have found a way around this problem. Is it something that is easy enough to walk me through? From what I can tell, you altered the actual CompositeSprite source to prevent multiple collision layers from conflicting. Any further guidance on this fix would be extremely appreciated; in the meantime, I'll do my best to explore and understand how the CompositeSprite is actually doing its work under the hood. Cheers!
P.S. Btw that game is really cool! That looks like something I would enjoy playing, if you ever want feedback or playtesting done be sure to contact me!
11/27/2013 (8:15 am)
Jonathan, your suggestions are always accurate and seem to come right on time. I have the noise and terrain working now, but this problem with the CompositeSprites is still looming over it all. If the character is walking across the terrain and bumps the right or left side of any block, it will make it so that when he travels across the top of blocks afterwards they will randomly block movement. You seem to have found a way around this problem. Is it something that is easy enough to walk me through? From what I can tell, you altered the actual CompositeSprite source to prevent multiple collision layers from conflicting. Any further guidance on this fix would be extremely appreciated; in the meantime, I'll do my best to explore and understand how the CompositeSprite is actually doing its work under the hood. Cheers!
P.S. Btw that game is really cool! That looks like something I would enjoy playing, if you ever want feedback or playtesting done be sure to contact me!
Associate Mike Lilligreen
Retired T2Der
Collision shapes are made at the SceneObject level. This means that the sprites inside of a CompositeSprite can't be directly assigned a collision shape. But since an object can have an unlimited number of collision shapes, you can simply "fake" it and create a collision shape at the exact location of each tile inside the composite. Just be aware of this disconnect, you won't be able to just select a sprite inside a composite to get the collision shape that belongs to it - but getting the local position of the sprite can get you the local position of the collision shape.
Here is a script I made that works:
function CompositeToy::create(%this) { %map = new CompositeSprite(); %map.setBatchLayout("rect"); %map.setDefaultSpriteStride(10, 10); %map.setDefaultSpriteSize(10, 10); for (%y = 0; %y <= 2; %y++) { for (%x = 0; %x <= 2; %x++) { %map.addSprite(%x SPC %y); %map.setSpriteImage("ToyAssets:tiles", 5); %position = %map.getSpriteLocalPosition(); %map.createPolygonBoxCollisionShape(10, 10, %position); } } %map.setPosition("-10 -10"); SandboxScene.add(%map); }