Rendering Images

The demo enables you to move or spin the image by careful use of the mouse or touch. The image is George Wright's GroundEnemy.png from TowerOfArcher. We keep the code short by using only a single body. Gravity is zero, so the image is stationary initially. Box2D calculates the position and orientation of the bounding rectangle and using this data we draw the TW3Image in the same way as for a canvas project without a physics engine.

Demonstration

Box2DImageDemo.html

If you do not see the demonstration, your school security system might have blocked it. Click here to try it on a separate page.

Smart Pascal Code

This code compiles with Versions 3.0 (with warnings) and 2.2 of Smart Mobile Studio.

unit Unit1;

interface

uses
  System.Types, SmartCL.System, SmartCL.Components, SmartCL.Controls, SmartCL.Application,
  SmartCL.Game, SmartCL.GameApp, SmartCL.Graphics, SmartCL.MouseTouch, SmartCL.Touch,
  Box2DWrapper, SmartCL.Controls.Image;

type
  TCanvasProject = class(TW3CustomGameApplication)
  private
    const FRAME_RATE = 1 / 60;
    const SCALE = 10;
    FWorld: Tb2World;
    FMousePos: Tb2Vec2;
    FIsMouseDown: Boolean;
    FMouseJoint: Tb2MouseJoint;
    FBody: Tb2Body;
    FImage: TW3Image;
  protected
    procedure ApplicationStarting; override;
    procedure ApplicationClosing; override;
    procedure PaintView(Canvas: TW3Canvas); override;
    procedure MouseDownHandler(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure MouseMoveHandler(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  end;

implementation

procedure TCanvasProject.ApplicationStarting;
var
  FixtureDef: Tb2FixtureDef;
  BodyDef: Tb2BodyDef;
begin
  inherited;
  FWorld := Tb2World.Create(
    Tb2Vec2.Create(0.0, 0.0),   // zero gravity
    True                        // allow sleep
  );
   // Create fixture definition (used to describe fixture objects)
  FixtureDef := Tb2FixtureDef.Create;
  FixtureDef.Density := 1.0;
  BodyDef := Tb2BodyDef.Create;
  FixtureDef.Shape := Tb2PolygonShape.Create;
  // Create a rectangular dynamic object.
  BodyDef.BodyType := btDynamicBody;
  FMousePos := Tb2Vec2.Create(0, 0);
  GameView.OnMouseTouchClick := MouseDownHandler;
  GameView.OnMouseTouchRelease := lambda FIsMouseDown := False; end;
  GameView.OnMouseMove := MouseMoveHandler;
  GameView.OnTouchMove := lambda(sender: TObject; td: TW3TouchData) FMousePos.SetXY(
    td.Touches.Touches[0].PageX  / SCALE, td.Touches.Touches[0].PageY / SCALE); end;

  FImage := TW3Image.Create(nil);
  FImage.LoadFromURL('res/GroundEnemy.png');
  FImage.OnLoad := procedure(o : TObject)
    begin
      Tb2PolygonShape(FixtureDef.Shape).SetAsBox(0.5 * FImage.Width / SCALE, 0.5 * FImage.Height / SCALE);
      BodyDef.Position.SetXY(0.5 * GameView.Width / SCALE, 0.5 * GameView.Height / SCALE);
      FBody := FWorld.CreateBody(BodyDef);
      FBody.CreateFixture(FixtureDef);
      GameView.Delay := 5;
      GameView.StartSession(False);
    end;
end;

procedure TCanvasProject.ApplicationClosing;
begin
  GameView.EndSession;
  inherited;
end;

procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
var
  MouseJointDef: Tb2MouseJointDef;
begin
  // Draw background
  Canvas.FillStyle := 'rgb(20, 20, 50)';
  Canvas.FillRectF(0, 0, GameView.Width , GameView.Height);
  // Handle mouse
  if FIsMouseDown and not Assigned(FMouseJoint) then
    begin
      MouseJointDef := Tb2MouseJointDef.Create;
      MouseJointDef.BodyA := FWorld.GetGroundBody;
      MouseJointDef.BodyB := FBody;
      MouseJointDef.Target.SetXY(FMousePos.X, FMousePos.Y);
      MouseJointDef.CollideConnected := True;
      MouseJointDef.MaxForce := 300.0 * FBody.GetMass;
      FMouseJoint := Tb2MouseJoint(FWorld.CreateJoint(MouseJointDef));
      FBody.SetAwake(True);
    end;
  if Assigned(FMouseJoint) then
    if FIsMouseDown then
      FMouseJoint.SetTarget(FMousePos)
    else
      begin
        FWorld.DestroyJoint(FMouseJoint);
        FMouseJoint := nil;
      end;
  // Advance and draw world
  FWorld.Advance(FRAME_RATE, 6, 2);

  if Assigned(FBody) then
    begin
      var Pos := FBody.GetPosition;
      var Theta := FBody.GetAngle;
      Canvas.Save;
      Canvas.Translate(Pos.X * SCALE, Pos.Y * SCALE);
      Canvas.Rotate(Theta);
      Canvas.DrawImageF(FImage.Handle, -FImage.Width / 2, -FImage.Height / 2);
      Canvas.Restore;
    end;
  FWorld.ClearForces;
end;

procedure TCanvasProject.MouseDownHandler(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  FMousePos.SetXY(X / SCALE, Y / SCALE);
  FIsMouseDown := True;
end;

procedure TCanvasProject.MouseMoveHandler(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  FMousePos.SetXY(X / SCALE, Y / SCALE);
end;

end.    

Programming - a skill for life!

Using the Box2D graphics engine for advanced games in Smart Mobile Studio