Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add colors to Box2D objects in Smart Mobile Studio

I want to add colors to dynamic objects in Box2D. It seems like some properties of dynamic objects have to be assigned in the rendering of the scene. I have played around with the Box2D demo but can't figure out how to add properties like colors (and bitmaps) ?

like image 828
Flemming Avatar asked Oct 01 '22 08:10

Flemming


1 Answers

The Box2d demo that ships with SmartMS uses the "DebugDraw" feature of Box2d. This feature just paints a representation of the object with some internal colors for "static object", "moving object" and "still object". I'm actually not sure if it is possible to override these...

What you need to do, is to replace FWorld.DrawDebugData (in the PaintView method) with the custom painting you'd like to use.

You can traverse all object like this:

  Body := FWorld.GetBodyList;  //Will give you first object in list

  while Assigned(Body) do
  begin
    //Do something with "Body" here...

    Body := Body.GetNext;  //Will give you next object in list
  end;

For instance:

  i := 0;
  Canvas.Font := '10pt verdana';
  Canvas.FillStyle := 'rgb(255,255,255)';
  Canvas.FillTextF('Count:' + IntToStr(FWorld.GetBodyCount), 30, 40, MAX_INT);

  Body := FWorld.GetBodyList;
  while Assigned(Body) do
  begin
    Canvas.FillTextF('i:' + IntToStr(i),                    30, 60+(i*12), MAX_INT);
    Canvas.FillTextF('Y:' + FloatToStr(Body.Position.Y, 2), 70, 60+(i*12), MAX_INT);

    Body := Body.GetNext;
    Inc(i);
  end;

To paint a sprite for various objects, you should use a TW3SpriteSheets class to handle that.

Add the sprite sheet image as a resource file to the project, and it will be extracted to the res folder when you compile.

Load the image like this:

  FSpriteSheet := TW3SpriteSheet.Create;
  FSpriteSheet.LoadImage('res/MySpriteSheet.png');

And use the image like this:

  if FSpriteSheet.Ready then
  begin
    FSpriteSheet.Draw(Canvas, 100, 100, 1); // draw tile #1
    FSpriteSheet.Draw(Canvas, 133, 100, 2); // draw tile #2
    FSpriteSheet.Draw(Canvas, 166, 100, 3); // draw tile #3
  end;

Edit:

In the sprite sheet, you have all the rotations need of your object.

enter image description here
(Image from: http://gameonaut.com/wordpress/2011/11/flixel-demo-creating-animated-and-rotated-sprites-from-an-un-rotated-animated-images/)

enter image description here
(Image from; http://windowsphone7developerguide.blograby.com/more-sprite-transforms-rotation-and-scaling/)

The Angle you get from Box2d is in radians. Use the build int function RadToDeg to get the angle as 0-360.

Canvas.FillTextF('A:' + FloatToStr(Body.Angle, 2),               140, 60+(i*12), MAX_INT);
Canvas.FillTextF('A:' + FloatToStr(RadToDeg(Body.Angle), 0),     220, 60+(i*12), MAX_INT);

The angle can be more than 360 degrees, and you would only like to know the value from 0 to 360. Thus use MOD:

 SpriteAngle := Round(RadToDeg(Body.Angle)) MOD 360;

Further the angle can be negative, so you would need to turn -1 into 359:

 if SpriteAngle < 0 then
    SpriteAngle := SpriteAngle + 360;

Finally. Since 360 degrees is the same as 0 degrees, you would like to use 0:

 if SpriteAngle = 360 then
    SpriteAngle := 0;

Now we have an angle from 0 to 359 degrees, and we would like to pick the right sprite based on the angle. If you have 32 sprites in your sprite sheet, then you will have 360/32 = 11.25 degrees per sector. So, for the first sector (0 to 11.25) we would like to use sprite 0, for the next sector (11.25 to 22.5) we would use the second sprite etc. By using Trunc we will get the sprite number we are looking for. 359/11.25 => 31.911 => 31

  FSpriteSheet.Draw(Canvas, X, Y, Trunc(SpriteAngle/11.25)); 

In this screenshot I used the sprite sheet from this article (http://www.codeproject.com/Articles/9012/Rotating-Sprite-Objects-on-DirectDraw-Wrapper-for). It has 101 sprites, and it is very smooth.
(Debugdata: i, Body.Position.Y, Body.Angle, RadToDeg(Body.Angle), Final 0-359 angle, SpriteID)

enter image description here
(Click to play screen cast)


Edit 2:

A few notes about X,Y and using the second half of the sprite sheet...

To avoid a mess, I left out X and Y from the previous code line.

Setting X and Y is basically:

X := Body.Position.X * CScale;
Y := Body.Position.Y * CScale;

However! If you run this with debugdraw, you'll notice that Body.Position.X and Body.Position.Y refers to the center of the Box2d object, while FSpriteSheet.Draw(Canvas, X, Y, ... refers to the top, left corner. You will therefor need to make a minor adjustment:

X := Round(Body.Position.X * CScale) - FSheet.SpriteHalfWidth;
Y := Round(Body.Position.Y * CScale) - FSheet.SpriteHalfHeight;

enter image description hereenter image description here


An how to pick a sprite from the second half of the sprite sheet?

Well, it's pretty easy ;-)

If you want to use images from the second half, you need to start the 0-indexed counting from the first sprite in the second set. There are 112 images in each set. First set = 0..111, second set = 112..223. So, when you want to reach the first set, use 0 + SpriteId. When you want to pick sprites from the second set, use 112 + SpriteId. And so on if you have more sets.

In my example, I used the second set for every second object. By using i MOD 2, every odd number would be 1, and every even number would be 0.

Thus, this code would result in 0 for every even number and 112 for every odd number:

SpriteSetStart := (i MOD 2) * 112;

Then we add the reference to the sprite id. In my example with 101 sprites (and 11 blank spites), each sprite would cover a sector of 3.564 degrees. 360/101 => 3.564.

SpriteId := Trunc(SpriteAngle/3.564);

Finally:

FSpriteSheet.Draw(Canvas, X, Y, SpriteSetStart + SpriteId); 
like image 146
Jørn E. Angeltveit Avatar answered Oct 13 '22 01:10

Jørn E. Angeltveit