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