Stepping the Simulation

Download Report

Transcript Stepping the Simulation

GrumpyBlocks:
A Tale of Writing a Game in AutoCAD Using ARX
Christer Janson
Sr. Software Architect – AutoCAD Products
© 2012 Autodesk
Class Summary
An annotated source code level review of two games written in AutoCAD
SpaceCADet – A modern take on the 1979 classic game
GrumpyBlocks – A 2D dynamics simulation environment with game play
aspects
© 2012 Autodesk
Learning Objectives
At the end of this class, you will be able to:
 Have entities interact with a simulation engine
 Write commands that are time-aware and interactive
 Waste hours playing (or designing) levels
 Continue develop GrumpyBlocks and SpaceCADet, or use them as a
templates to write fun or useful apps for our AppStore(*)

(*) Yes, full source code to everything(**) presented is in your handouts
 (**) No, sample image and audio files cannot be distributed
© 2012 Autodesk
So who am I?
Grew up with Computers –
1981 – Commodore Vic 20
1983 – IBM PC
1985 – Commodore Amiga 1000
1990 – Autodesk (Sweden)
1994 – Autodesk (Neuchatel)
1997 – Autodesk (US)
3ds Max (1.0 – R5), Impression, AutoCAD (R10 – R13 + 2010 – …)
Point Clouds, Graphics, AcDb, etc.
© 2012 Autodesk
Why write a game in AutoCAD?
AppHack!!
March 2012 – Four day AutoCAD ‘hackathon’ contest.
Thursday noon – Sunday night (Monday morning)
Initial plan was to write an FPS game with 3D Point Cloud maps
(30 minutes later I changed my mind)
First day: SpaceCADet – Asteroids in AutoCAD
Second day – forked the code into GrumpyBlocks
rd
Both apps finished, won 3 price
Sadly not posted to AppStore…
© 2012 Autodesk
“As is”
No post contest cleanup (experimental/dead code)
No Warranty Expressed or Implied!
Code written during a four day contest with free beer
© 2012 Autodesk
SpaceCADet
(demo)
© 2012 Autodesk
Implementation

Command: SPACECADET
 Written from scratch in less than 15 hours
 Basic ObjectARX app for AutoCAD 2013
 C++, Win32, ObjectARX
 AutoCAD geometry as game objects (Named Blocks)

3D Visual Style and Hardware Acceleration required!
© 2012 Autodesk
AutoCAD
SpaceCADet.arx
Init
Run Loop
Cleanup
Utilities
&
Support Code
GameBoard (Space)
Functions
Objects
Actions
Ship
Simulation
Stepping
Asteroids
Collision
Detection
Support Code
(Bounce/Break/…)
PhotonTorpedoes
Explosions
Reserve Fleet
Score
© 2012 Autodesk
Not a game, a Game Construction Kit

Game pieces defined in current drawing

“Ship”, “ShipThrust”, “Torpedo”, “Asteroid”, “Explosion”

Media (named sound files) in drawing folder
 Some hardcoded mechanics: Score, Number of lives, levels, etc.
 Timings based on FrameRate (30fps) / FrameTime (1/30 s)

Increasing frame rate does not affect game play
© 2012 Autodesk
Framework

Command Start

Init()
 MainLoop()
 Cleanup

Command End
© 2012 Autodesk
Init

Drawing Setup

Zoom level
 Grid Off
 Tilemode
 Capture Screen Size
 Get BlockTableRecords for game pieces
 Create and populate GameBoard (new class Space())
 Start the game
© 2012 Autodesk
Main Loop

Get clock – clock_t clock()


15ms accuracy is poor – Use QueryPerformanceCounter() for higher resolution
Get Keyboard Action


GetAsyncKeyState() check if given key is pressed
Call gameboard with given Action, if any

updateDisplay()
 Run custom Windows Event Loop

Need to eat keystrokes pressed during game play

Wait until clock() > frameTime for given frame rate (30->22 fps)
 Step the simulation
© 2012 Autodesk
SpaceCADet Source Review
Command / Init() / Run()
SpaceCADet.cpp
© 2012 Autodesk
Game Board Contents

Represented by class Space() – “The Game Object”

Host Interface
 Ship, Score
 Std::list<>
 Asteroids, PhotonTorpedoes, Explosions, ReserveFleet
 ObjectIds for geometry
 Ship, ShipThrust, Asteroid, Explosion, Score
 View Information
© 2012 Autodesk
Game Board Public Methods

Populate()
 Step()
 Actions

Speedup, TurnLeft, TurnRight, Fire, HyperSpace

Get/SetGeometry()
 Start()
© 2012 Autodesk
Game Pieces

ThingInMotion (Base class for Ship, Asteroid, PhotonTorpedo)

AcGePoint3d
 AcGePoint3d
 AcGeVector3d
 Double



pos
lastPos
direction
speed
Move()
MoveAbs()
Virtual Radius() = 0
© 2012 Autodesk
Game Pieces – Ship : ThingInMotion

Represented by two separate blocks (Ship, ShipThrust)



Ship::SetThrust(bool onOff)
AcDbBlockReference::setVisibility() ensures only one is visible
ObjectId
 ObjectId
 double
 double
mGeometry
mThrustGeometry
mSpeed
mAngle // Ship angle, not direction of movement
© 2012 Autodesk
Game Pieces – Asteroid : ThingInMotion

enum Size { kBig, kMedium, kSmall };
 Size
mSize
 ObjectId mGeometry
 double
mSpeed
 double
mAngle
 double
mAngularVelocity
© 2012 Autodesk
Game Pieces – PhotonTorpedo : ThingInMotion

ObjectId
 double
 double
 double
 double
mGeometry
mSpeed
mAngle
mAngularVelocity
mAge
bool
CheckAge() – Expires after certain age
 Age is distance based so that a torpedo never hits the ship

© 2012 Autodesk
Game Pieces – Explosion

ObjectId
 AcGePoint3d
 double
 double
 bool
mGeometry
mPos
mRadius
mAge
mMajor – Ship explosion is massive

CheckAge() – Expires after certain age
GrowOlder() – Increase size by age
bool
 void
© 2012 Autodesk
Game Pieces – Score

ObjectId
 int
mGeometry - AcDbText
mScore

SetScore()
GetScore()
Add(int points)
void
 int
 void
© 2012 Autodesk
Simulation Setup

Populate()



Create ship at center of screen
Create Asteroids
 Create asteroids randomly between inner and outer radius
 “Asteroid free zone” around ship to prevent immediate collision
 Number of asteroids: 3 + level
Create fleet of reserve ships (number of lives)
 ReserveFleet displayed in top left corner of screen
© 2012 Autodesk
Stepping the Simulation : Space::Step()

Called by Main Loop at 30fps (or less since we don’t drop frames)

mShip->Move() - Using Ship direction and velocity
 Turn off Ship thrust geometry if we are not thrusting
 If we are dead but have reserve ships, and 2 seconds has passed, create new
Ship. If there are no more ships, GameOver!
 Iterate over Asteroids, and call asteroid->Move() on each asteroid
 Iterate over PhotonTorpedoes, check age and destroy if they are too old,
otherwise call torpedo->Move() (increases age as well)
 Iterate over Explosions, check age and destroy if they are too old, otherwise call
explosion->GrowOlder()
 Test for Collisions
© 2012 Autodesk
Collision Detection

Using object position and radius
 Test for distance squared avoids square root for every test
 Bounding circle



Threshold = (radius1+radius2)^2
DistanceSquared = (pos1-pos2).lengthSqrd()
Collision Yes/No = distSq < Threshold
© 2012 Autodesk
Collision Detection

Test Ship against all Asteroids


Hit causes ship destruction
Test all PhotonTorpedos against all Asteroids

Hit causes a breakup of the Asteroid.
 Small asteroids are destroyed. Bigger split into two smaller.

Test all Asteroids against each other

Hit causes both Asteroids to change direction (bounce)
© 2012 Autodesk
Actions and reactions

Speedup() – Called each frame when thrusting

Ship->SetThrust(true) // Turned off by Step if no longer active
 Using ship angle, compute offset vector
 Add offset vector to current velocity/direction vector
 New speed = vector length. New direction = normalized vector
Turn Left/Right – Change ship angle
 Fire()


No more than 10 torpedoes at the same time
 Determine direction & velocity from ship direction and motion
 Create new PhotonTorpedo
© 2012 Autodesk
SpaceCADet GameBoard Source Review
Populate() / Step() / TestAllForCollision()
GamePieces.cpp
© 2012 Autodesk
GrumpyBlocks
(or Blocks With Enemies)
(Sample file demo)
© 2012 Autodesk
Implementation

Written from scratch in 3 days (40 hours (give or take))
 Basic ObjectARX app for AutoCAD 2013
 C++, Win32, ObjectARX, Box2D Dynamics Engine (More later)

(Incidentally the same dynamics engine used in a game featuring birds and pigs)

2D dynamics simulation with AutoCAD geometry
 3D Visual Style and Hardware Acceleration required!
© 2012 Autodesk
Not really a game, a 2D Simulation Environment
(With gameplay aspects)

Run simulations on existing geometry
 Simulation can be ‘free running’ or ‘initiated by projectile’
 “Tags” on objects describe non-default behavior
 Model Space Only
 Preferred coordinate space (0,0)-(10,10)




AutoCAD units are interpreted by the simulation engine as Meters
Larger coordinates creates sluggish simulation
Any object with extents outside (-40,-40)–(40,40) is excluded
If you want use Box2D outside of those coordinates you need a custom objectworld transform
© 2012 Autodesk
Commands

GRUMPYBLOCKS
 GRUMPYTAG
 GRUMPYFORCE
 GRUMPYJOINT
 GRUMPYLIST
 GRUMPYSETUP
- Start simulation
- Tag object property
- Add a keyboard assigned force
- Add a joint between two objects
- List Dynamics Properties
- Global properties
© 2012 Autodesk
Supported objects
Good
Bad

AcDbCircle
 AcDbPolyline – 3-8 vertices – Convex hull only!!

Need to be Closed, oh, and no arcs please
 Both CW and CCW is okay – converted internally

AcDbRasterImage


Rectangular collision (but can be put in a block with custom collision hull)
AcDbBlockReference

Blocks acts as single object in the simulation
 Allows complex, or unsupported objects to participate in the simulation
 Need embedded object tagged as Convex Hull to work
© 2012 Autodesk
Object ‘Type’

Dynamic
 Static
 Hull
 Invisible Hull
 Ground Plane
 Excluded
 Friend
 Enemy
 Prop
- Moving dynamic object
- Static object, affects collision
- Blocks: Collision hull
- Blocks: Invisible Collision Hull
- If exists, simulation will not create default ground plane
- Will not participate in simulation
- Can be destroyed, lose game if all destroyed
- Can be destroyed, win game if all destroyed
- Can be destroyed, does not affect win/lose
© 2012 Autodesk
Box2D Primer

Open source C++ engine simulating rigid bodies in 2D. [http://box2d.org]
 Convex polygons and circles
 Contacts, forces, friction, joints, motors, etc.
 Body

Class b2Body
 Rigid unbreakable object.
 Has one or more shapes

Shape

Class b2Shape
 Convex polygon or circle
© 2012 Autodesk
GrumpyBlocks Dynamics support

Gravity
 Collision based on polygon or circle
 Customizeable per-object parameters

Density, Friction, ‘Bounce’, Type

Rotational Joints linking objects together (chains, etc.)
 Impulse Forces with keyboard assignment allows the user to affect the
simulation
© 2012 Autodesk
Custom Data

MasterObject - Custom AcDbObject in Named Object Dictionary


PropertyTag - Simulation related per-object data



Properties kept as XDATA on participating entities
Modified using GRUMPYTAG command
GrumpyJoint


Keep track of global properties for the drawing
- Custom AcDbEntity in database
Gizmo for Joints between two simulation objects
GrumpyForce

Gizmo for impulse forces assigned to keys
© 2012 Autodesk
GrumpyBlocks
(or Blocks With Enemies)
Simulation creation demo
Types, Block Hull, Tags, Properties
© 2012 Autodesk
AutoCAD
GrumpyBlocks.arx
Init
Simulation (World)
Functions
Objects
Triggers
Static
Cleanup
Simulation
Stepping
Dynamic
Utilities
&
Support Code
Draw
Run Loop
ProcessContactList
ShapeCreation
© 2012 Autodesk
But how does it actually work?
Acad Entity
Step Simulation
Dynamic
Step Simulation
Loop
Check Collisions
Draw Simulation
Update Entities
transformBy
updateDisplay
© 2012 Autodesk
Fundamental Classes
Class World – Simulation Environment
 Class DynamicsObject – Object represented in simulation



Class Static : DynamicsObject
 Unmovable rigid object, participates in collision
 ObjectId, Body, Shape
Class Dynamic : DynamicsObject
 Fully interacting in simulation, affected by forces and collisions
 ObjectId, Body, Shape
 Position, Angle, Type, Life
 LinkedFollowers (Forces)
 Update() – Transfer simulation result to entities
© 2012 Autodesk
Init : Populating the Simulation

GetAllCandidates()

Walk model space BTR
 Collect all relevant objects:
 AcDbPolyline, AcDbCircle, AcDbRasterImage, GrumpyJoint, GrumpyForce and
AcDbBlockReference
 For AcDbBlockReference step into definition and look for:
 Hull object
 GrumpyForce and GrumpyJoint
© 2012 Autodesk
Init : Populating the Simulation


For each object found:
 Check for XDATA propertyTag
 Collect Force and Joint objects into individual lists
 Create World representation for each entity
 mWorld->CreateFromPolyline(ObjectId, PropertyTag)
 mWorld->CreateFromRasterImage(ObjectId, PropertyTag)
 mWorld->CreateFromCircle(ObjectId, PropertyTag)
 mWorld->CreateFromBlockReference(ObjectId, Hull, PropertyTag);
Create Joint and Force representations
 mWorld->CreateJoint(ObjectId);
 mWorld->CreateForce(ObjectId);
© 2012 Autodesk
Populating the Simulation – Geometry->Shape

CreateFrom[Polyline/RasterImage/Circle/Block]
 Create a Shape with collision hull
 Create a new Static or Dynamic object depending on PropertyTag
 Create Body, hook up Shape, set properties
 Add Static or Dynamic to World
 Objects added at rest with no initial force (except missile)
© 2012 Autodesk
GrumpyBlocks Source Review
GetAllCandidates()
CreateFromPolyline()
class World, class Static, class Dynamic
© 2012 Autodesk
Initializing the Simulation

If (PickMissile()) - Pick object and drag to throw position
CaptureMissileState() – Clone Missile Entity
 Compute force vector from Missile and Throw positions
 ApplyLinearImpulse(forceVector) – This will kick off the missile

© 2012 Autodesk
Running the Simulation (Almost same as SpaceCADet)
Get clock – clock_t clock()
 Get Keyboard Action (Force Triggers)
 Run custom Windows Event Loop


Need to eat keystrokes pressed during game play

Wait until clock() > frameTime for desired frame rate
 Step the simulation
 Draw the simulation (different from SpaceCADet)
 updateDisplay()
© 2012 Autodesk
Stepping the Simulation : World::Step()
Called by Main Loop at 30fps (or less since we don’t drop frames)
 Call Box2DWorld::Step()

This runs the dynamics simulation for 1/30th of a second
 Position and Velocity Iterations are customizable (Higher = Slower)
 New position (and internal velocity), is computed by Box2D
 Collisions are added to ‘contact list’


ProcessContactList()

Loop through contacts and check if a hit dynamic is killed
 Force is computed by Mass, Inertia and LinearVelocity
 Kill is computed by deducting Force from Life property
 Killed objects are removed from simulation (and their entities deleted)
© 2012 Autodesk
GrumpyBlocks Source Review
Step()
ProcessContactList()
© 2012 Autodesk
Draw the Simulation : World::Draw()

Called by Main Loop at 30fps
 Dynamics::Update()






Snag: AutoCAD deals only with delta transformations while Box2D only deals
with absolute transforms. Keep curPos and curAngle in Dynamic so that we can
transform the entity with a delta
Get new Rotation and Translation from Body::Position / Body::Angle
Compute Transform Delta of old -> new transform
Open Entity, call transformBy(), make note of new pos/rotation
Do the same for any linked Followers (Forces)
Check if any objects are ‘Awake’

None means that simulation is at rest and we can exit
© 2012 Autodesk
GrumpyBlocks Source Review
Draw()
© 2012 Autodesk
Source Code and Samples
Sample images are for demonstration purposes only
Please do not distribute!
Source code – go ahead, make awesome apps!
© 2012 Autodesk
Questions?
© 2012 Autodesk
Autodesk, AutoCAD* [*if/when mentioned in the pertinent material, followed by an alphabetical list of all other trademarks mentioned in the material] are registered trademarks or trademarks of Autodesk, Inc., and/or its subsidiaries and/or affiliates in the USA and/or other countries. All other brand names, product names, or trademarks belong to their respective holders. Autodesk reserves the right to alter product and
services offerings, and specifications and pricing at any time without notice, and is not responsible for typographical or graphical errors that may appear in this document. © 2012 Autodesk, Inc. All rights reserved.
© 2012 Autodesk