@revenant chaos
Here you go.
`--------------------------------------------------
` NEWTON PHYSICS SDK WRAPPER
` DEMO PROJECT 5 - first person shooter example
`
`--------------------------------------------------
`standard DBPro startup code...
sync on
sync rate 60
autocam off
set ambient light 100
set point light 0, 50, 100, 20
set light range 0, 5000
randomize timer()
`load images
load image "..\media\floor.png",1
load image "..\media\fps\bullethole.png", 2
`set camera start point
position camera 0.0, 5.0, -20.0
`this is just an array so I can write onscreen if the simulation is running or not.
`it has nothing to do with Newton at all.
dim GOSTR$(2)
GOSTR$(1) = "SIMULATION PAUSED"
GOSTR$(2) = "SIMULATION RUNNING"
`variables used for the first-person camera
global Player = 0
global Player_Mass# = 12.0
global Player_Speed# = 10.0
global Player_Yangle# = 0.0
global Player_Xangle# = 0.0
global Player_ShootPower# = 5000.0
global Player_JumpSpeed# = 35.0
global Door = 0
global DoorSlider = 0
global Default as integer
global PlayerID as integer
global Player_Run_Speed as float = 5.0
global Player_Walk_Speed as float = 2.5
slowdown#=0.93
`I am using GOTO in these demos to keep them simple, in a real game situation you
`would probably want to avoid this programming style.
`------------------------------------------------
`------------------------------------------------
TOP:
`Initialize Newton, and create the Newton World.
NDB_NewtonCreate
`Set the temp vector to our gravity constant (acceleration due to gravity)
`then set as our standard gravity force.
NDB_SetVector 0.0, -50.0, 0.0
NDB_SetStandardGravity
`make the test room!
gosub MakeRoom
`make the player (camera) body used for player movement!
gosub MakePlayer
` the first time you call this command, you may get a very large time#,
` so I call it twice at the start, to make sure we're getting a nice small time# value.
time# = NDB_GetElapsedTimeInSec()
time# = NDB_GetElapsedTimeInSec()
`this variable is used for pausing/unpausing the simulation.
GO = 1
` -----------------------------------
` MAIN LOOP
` -----------------------------------
do
`get the elapsed time since last frame and save into time# variable.
time# = NDB_GetElapsedTimeInSec()
` Here's the big command that updates the Physics system.
` Objects are moved and positioned automatically.
` the if GO=1 part is just a variable check so we can pause and unpause the system.
if GO = 1 then NDB_NewtonUpdate time#
`if user presses "q" key, quit the program!
if lower$(inkey$()) = "q" then exit
`if user presses "r", we need to reset. so first destroy the Newton World, which will in turn
`delete all of the DBpro objects for us. then goto TOP, and start over!
if lower$(inkey$()) = "r"
NDB_NewtonDestroy
goto TOP
endif
`this part is for pausing/unpausing. basically it sets the GO variable to 1 or 0.
if keystate(25) and P_PRESSED = 0 then P_PRESSED = 1
if P_PRESSED = 1
P_PRESSED = 2
inc GO, 1
if GO > 1 then GO = 0
endif
if keystate(25) = 0 then P_PRESSED = 0
`draw the text in the upper left part of the screen.
gosub DrawOnscreenData
`handle player movement and jumping!!
gosub HandleCamera
`handle shooting objects!
gosub Shooting
`handle the automatic door!
gosub DoorCheck
`NEWTON DEBUGGING SYSTEM:
` This may be a bit confusing to some users... if you don't understand it, ignore it for now.
` the DebugMakeNewtonObject command is really cool. it takes the entire Newton world, and makes
` it into a single DBpro object, so you can see the "world according to Newton" onscreen. you can
` use this to make sure that your visual objects and your rigid bodies are lined up, and also see the
` actual shape of the object according to Newton. Here I check if the control key is down, and if
` so I make the newton object, and save it into the "obj" variable. then I update the screen with
` te sync command, and then delete the "obj" right after syncing.
` there is also another debug command called DebugDrawNewtonLines, which draws the lines in 2D. it's
` slower than this command, but you don't have to fiddle with making and deleting objects. see the docs.
if controlkey()
obj = NDB_DebugMakeNewtonObject()
set object light obj, 0
else
obj = 0
endif
`update the screen
sync
if obj <> 0
delete object obj
endif
loop
` -----------------------------------
` -----------------------------------
` Destory the Newton world, clearing up any memory that was allocated.
NDB_NewtonDestroy
end
MakeRoom:
`this example uses TreeCollision objects for the level. Tree Collisio objects are just another way of
`describing collision shapes to Newton... but instead of using a primitive shape, it uses the polygon data
`from a 3D model. in Newton TreeCollision objects can only have a mass of 0, so they cannot be dynamic objects.
`this is fine for background pieces, which should never move anyway.
`Step 1- load the model to make the collision from. In this case it's also out visual model, but you might
` want to use another model specifically for collision. Also see the docs for info about the Serialization
` system, which can make loading TreeCollision objects MUCH faster for large, complex models.
obj = FreeObject()
load object "..\media\fps\room2.x", obj
obj2 = FreeObject()
load object "..\media\fps\room2_lmap.x", obj2
ghost object on obj2, 1
disable object zwrite obj2
set object light obj, 0
set object ambient obj, 0
set object light obj2, 0
set object ambient obj2, 0
`Step 2- make the TreeCollision data from the object, and save into variable "Col"
Col = NDB_NewtonCreateTreeCollision( obj )
`Step 3- make the rigid body from the collision data
Room = NDB_NewtonCreateBody( Col )
`next you would position the object + rigid body, but we're leaving them at 0,0,0 for this demo.
`Step 4- finally, attach the object to the Rigid Body
NDB_BodySetDBProData Room, obj
NDB_NewtonBodySetDestructorCallback Room
NDB_NewtonReleaseCollision Col
`that's it! the TreeCollision body is ready, so our level will have collision!
`next we want to add a sliding door to our level. we will make the door from a simple Box rigid body,
`and connect it to the level body via a SLIDER joint, as we want a sliding door.
Door = MakeBox( 18.5, 0.0, -11.0, 13.0, 14.0, 1.0, 0.0, 0.0, 0.0, 25.0 )
NDB_BodySetGravity Door, 0
`When making a joint, you generally set vector 1 to the location of the joint, and vector 2 to a unit vector
`describing the direction of the joints`s "pin". see the docs for more information on all joints.
NDB_SetVector 1, 18.5, 0.0, -11.0
NDB_SetVector 2, 0.0, 1.0, 0.0
DoorSlider = NDB_NewtonConstraintCreateSlider( Door, Room )
NDB_SetSliderLimits DoorSlider, -1.0, 20.0
NDB_SetSliderMotorAccel DoorSlider, -50.0
`finally, add a few crates around to shoot at!
x# = -40.0
y# = 18.0
z# = 35.0
level = 1
boxes = 3
repeat
startz# = z# + -((boxes*5.0) / 2.0)
for i=1 to boxes
Body = MakeBox( x#, y#, startz#+(i*6.0), 5.0, 5.0, 5.0, 0.0, 0.0, 0.0, 25.0 )
next i
inc y#, 6.0
dec boxes
inc level
until boxes = 0
x# = -20.0
y# = -5.0
z# = -30.0
level = 1
boxes = 3
repeat
startz# = z# + -((boxes*5.0) / 2.0)
for i=1 to boxes
Body = MakeBox( x#, y#, startz#+(i*6.0), 5.0, 5.0, 5.0, 0.0, 0.0, 0.0, 5.0 )
next i
inc y#, 6.0
dec boxes
inc level
until boxes = 0
return
`This function makes the rigid body that represents the player
MakePlayer:
`for the player (camera), we're going to use an ellipsoid shape. this is a sphere that is "streched"
`on one or two axis'. in this case, we'll make it much taller than it is around, to mimick a human.
Sphere = NDB_NewtonCreateSphere( 2.5, 6.0, 2.5 )
Player = NDB_NewtonCreateBody( Sphere )
`setting the initial starting position.
NDB_BuildMatrix 0.0, 0.0, 0.0, 0.0, -1.0, -25.0
NDB_NewtonBodySetMatrix Player
`setting the mass...
NDB_SetVector 6.0, 8.0, 6.0
NDB_CalculateMIBoxSolid Player_Mass#
NDB_NewtonBodySetMassMatrix Player, Player_Mass#
`Newton automatically "freezes" objects which have come to rest, which means they are no longer calculated
`each time you call NDB_NewtonUpdate. they start calculating agani when another object hits them and sets
`them in motion again. However with our Player rigid body, we want it to be moved by the user's keyboard input.
`if the body freezes, Newton will not let the user move the body, because it's not in the active body list.
`To make a long story short, we need to tell Newton NOT to freeze this body, EVER. This will make Newton
`calculate this body every call to NewtonUpdate. this of course takes processor power, so it's best not to
`call this for too many bodies.
NDB_NewtonBodySetAutoFreeze Player, 0
NDB_BodySetGravity Player, 1
`Here's the meat and potatoes of the character system: the UpVector joint. this joint allows the character to
`move in 3 dimensions, but won't allow it to "fall over" when it hits other objects. which is exactly what we want
`here. for more info, have a look at the joint example and the documentation.
NDB_SetVector 0.0, 1.0, 0.0
UpVector = NDB_NewtonConstraintCreateUpVector( Player )
`Finally, because we want to control the player manually, we don't want friction getting in our way and causing
`problems... so I remove all friction between the Player material, and the Default material (aka everything else).
`this makes it MUCH easier to calculate the forces needed to move the player later.
Default = NDB_NewtonMaterialGetDefaultGroupID()
PlayerID = NDB_NewtonMaterialCreateGroupID()
NDB_NewtonMaterialSetDefaultFriction Default, PlayerID, 0.8, 0.8
NDB_NewtonMaterialSetDefaultElasticity Default, PlayerID, 0.01
`after setting the materials, we need to apply the Player material to the Player rigid body.
NDB_NewtonBodySetMaterialGroupID Player, PlayerID
return
`this subroutine makes the simple in-screen display, which explains how to use the demo.
DrawOnscreenData:
box 5,5,315,112,rgb(0,0,0),rgb(100,100,100),rgb(0,0,0),rgb(100,100,100)
line 5,5,315,5
line 5,5,5,112
line 315,5,315,112
line 5,112,315,112
text 10,10,"Newton Game Dynamics Wrapper version:"+str$(NDB_GetVersion())
text 10,20,"FIRST PERSON SHOOTER DEMO"
text 10,40," move with W,S,A,D Keys || Look with mouse"
text 10,50," LBM to shoot || Hold CTRL for debug data"
text 10,60," SHIFT to run || SPACE to jump || "p" to (un)pause"
text 10,70," Press "r" to reset || press "q" to quit"
text 10,80," FPS:"+str$(screen fps()) + " Body Count:"+str$(NDB_DebugRigidBodyCount())
text 15,95,GOSTR$(GO+1)
ink rgb(255,0,0), rgb(0,0,0)
line 320, 220, 320, 260
line 300, 240, 340, 240
ink rgb(255,255,255), rgb(0,0,0)
return
`This is the important subroutine that controls the Player movement. I will try to explain it in some detail.
`Basically the setup is like this: I have the Player rigid body I made in the MakePlayer subroutine, which is
`an elipsoid object that always stands up. Each game loop, I place the camera in same position as that Rigid
`Body. I also have 2 variables that represent the camera's orientation: Player_Yangle# and Player_Xangle#.
`these are linked to the mouse, to allow the user to look around. Then, when the user wants to move, I
`use the camera to deterime which way the player is facing, and then apply a force to the body in the direction
`of movement. The force is calculated in such a way that the player moves at a constant speed when walking.
`
`Here's how we calculate the movement force:
`
` In physics there is the formula F = m * a, where F is the force, m is the mass, and a is the acceleration.
`
` we also know that a (acceleration) is a CHANGE in velocity. so we can write "a" as (v2-v1)/time.
` this is basically (goal_velocity - current_velocity) / time.
`
` so in this part, after I calculate the direction I want to go, I get the current velocity, and use that
` to find the necessary acceleration to achieve that velocity. then I simlply plug that into Newton, and it
` takes care of the rest! I'll write more comments as I go to hopefully make it understandable.
HandleCamera:
`-------------------------
`CAMERA
`-------------------------
`these variables rotate the camera.
inc Player_Xangle#, mousemovey() * 0.25
inc Player_Yangle#, mousemovex() * 0.25
position mouse 320, 240
`put a limit on how much the player can loop up/down.
if Player_Xangle# > 80.0 then Player_Xangle# = 80.0
if Player_Xangle# < -80.0 then Player_Xangle# = -80.0
rotate camera Player_Xangle#, Player_Yangle#, 0.0
`here we get the position of the Player rigid body, and place the camera there.
NDB_BodyGetPosition Player
position camera NDB_GetVector_X(), NDB_GetVector_Y(), NDB_GetVector_Z()
`next we set the movement variables to zero. the will stay at zero unless the user pressed a movement key.
`player movement defaults to zero each loop.
MoveX# = 0.0
MoveZ# = 0.0
if keystate(17) or keystate(31)
`player is pressing "w" or "s" key, we need to move forward or backward!
`find direction vector my moving the camera...
`i use some trig to find the direction the player wants to move.
dx# = sin(Player_Yangle#)
dz# = cos(Player_Yangle#)
`then I add this to the move vector, making it positive for forward, negative for backward.
inc MoveX#, dx# * (keystate(17)-keystate(31)) :`makes a positive vector with "w", negative with "s"
inc MoveZ#, dz# * (keystate(17)-keystate(31))
endif
if keystate(30) or keystate(32)
`same thing here, but this is for strafing, so I add 90 degrees to the current angle!
dx# = sin(Player_Yangle#+90.0)
dz# = cos(Player_Yangle#+90.0)
inc MoveX#, dx# * (keystate(32)-keystate(30)) :`makes a positive vector with "d", negative with "a"
inc MoveZ#, dz# * (keystate(32)-keystate(30))
endif
`Now we need ouy direction vector the be a unit vector, so we divide by the length of the vector.
`if you are confused here, please do a little research on vector math, it's VERY important for any 3D
`application!
`finally, re-normalize the move vector
length# = sqrt( (MoveX#^2)+(MoveZ#^2) )
MoveX# = MoveX# / length#
MoveZ# = MoveZ# / length#
`okay, new let's find the current velocity of the player. that's easy through newton...
NDB_NewtonBodyGetVelocity Player
CurrentVel_X# = NDB_GetVector_X()
CurrentVel_Y# = NDB_GetVector_Y()
CurrentVel_Z# = NDB_GetVector_Z()
NDB_SetVector 1,CurrentVel_X#*slowdown#,CurrentVel_y#*1.0,CurrentVel_z#*slowdown#
NDB_NewtonBodysetVelocity Player
`and our goal velocity is simply our move direction multiplied by the player's walking speed. (or 2x if running)
s#=(player_run_speed*(shiftkey())+player_walk_speed*(1-shiftkey()))
AccelX# = MoveX#*s#/time#
AccelZ# = MoveZ#*s#/time#
`also we want to limit how big the acceleration can be. this keeps the player from being able to push
`really heavy objects, etc.
if AccelX# > 150.0 then AccelX# = 150.0
if AccelX# < -150.0 then AccelX# = -150.0
if AccelZ# > 150.0 then AccelZ# = 150.0
if AccelZ# < -150.0 then AccelZ# = -150.0
`finally, we just need to apply this force to the Player! we'll use the AddForceGlobal command.
`NOTE- in the formula above, I said F = m * a. we calculated "a", but we never multiplied by the mass!
` this is because my wrapper does this for you. it automatically multiples the force by the body
` mass inside the function.
`set temp vector 1 to force location
NDB_BodyGetPosition Player
`set temp vector 2 to force direction
NDB_SetVector 2, AccelX#, 0.0, AccelZ#
NDB_BodyAddForceGlobal Player
`we also want jumping to be available. for this we will use a simple ray cast to determine if we're
`standing on the ground or not.
AccelY# = 0.0
if spacekey() and SPACEPRESSED = 0
SPACEPRESSED = 1
`user has pressed the space bar, apparently they would like to jump :)
`cast a ray from the player location straight down, see if we hit something.
NDB_BodyGetPosition Player
px# = NDB_GetVector_X() : py# = NDB_GetVector_Y() : pz# = NDB_GetVector_Z()
NDB_SetVector 1, px#, py#, pz#
NDB_SetVector 2, px#, py# - 6.05, pz#
dist# = NDB_NewtonWorldRayCast()
if dist# < 1.0
`something has been found! in this case, we don't care what, just let the player jump!
`the jump part is just a simple upward force..
AccelY# = ((Player_JumpSpeed# - CurrentVel_Y#) / time#)
`add another force to the player, the jumping force! this is just like above, using the
`new AddForceGlobal command! easy, huh?
NDB_BodyGetPosition Player
NDB_SetVector 2, 0.0, AccelY#, 0.0
NDB_BodyAddForceGlobal Player
endif
endif
if spacekey() = 0 then SPACEPRESSED = 0
`and there you go!! FPS character controlling in remarkably few lines :)
return
Shooting:
if mouseclick() = 1 and MOUSE = 0
MOUSE = 1
`user wants to shoot! let's cast a ray with Newton and see if we've hit something!
x1# = camera position x() : y1# = camera position y() : z1# = camera position z()
`get a unit vector in the direction the player is facing, by moving the camera 1 unit!
move camera 1.0
x2# = camera position x() : y2# = camera position y() : z2# = camera position z()
move camera -1.0
`we will cast a ray 500 units long.
dx# = x2# - x1#
dy# = y2# - y1#
dz# = z2# - z1#
x2# = x1# + (dx# * 500.0)
y2# = y1# + (dy# * 500.0)
z2# = z1# + (dz# * 500.0)
`cast the ray. put start point in vector 1, end point in vector 2
NDB_SetVector 1, x1#, y1#, z1#
NDB_SetVector 2, x2#, y2#, z2#
dist# = NDB_NewtonWorldrayCast()
if dist# < 1.0
`something hit!
HitBody = NDB_RayCastGetBody()
castdist# = dist# * 500.0
cast_x# = x1# + (dx# * castdist#)
cast_y# = y1# + (dy# * castdist#)
cast_z# = z1# + (dz# * castdist#)
hitmass# = NDB_NewtonBodyGetMassMatrix( HitBody )
if hitmass# > 0.0
`this is a live object, give it a kick!
NDB_SetVector 1, cast_x#, cast_y#, cast_z#
NDB_SetVector 2, dx# * Player_ShootPower#, dy# * Player_ShootPower#, dz# * Player_ShootPower#, 0.0
NDB_BodyAddForceGlobal HitBody
NDB_NewtonWorldUnfreezeBody HitBody
else
`this is the background (mass=0), add a bullethole!
obj = FreeObject()
make object plain obj, 0.5, 0.5
position object obj, cast_x#-(dx#*0.01), cast_y#-(dy#*0.01), cast_z#-(dz#*0.01)
NDB_RayCastGetNormal
nx# = NDB_GetVector_X() : ny# = NDB_GetVector_Y() : nz# = NDB_GetVector_Z()
x# = cast_x# + nx# : y# = cast_y# + ny# : z# = cast_z# + nz#
point object obj, x#, y#, z#
texture object obj, 2
set object transparency obj, 1
endif
endif
endif
if mouseclick() = 0 then MOUSE = 0
text 10,300,"HitBody:"+str$(HitBody)
text 10,310,"HitMass:"+str$(hitmass#)
return
`this subroutine simply checks the distance (on the XZ plane) between the Player and the Door. if it's within
`15 units, it sets the door to open. if it's not, it sets the door to close. simple.
DoorCheck:
NDB_BodyGetPosition Player
x1# = NDB_GetVector_X() : z1# = NDB_GetVector_Z()
NDB_BodyGetPosition Door
x2# = NDB_GetVector_X() : z2# = NDB_GetVector_Z()
dx# = x2# - x1#
dz# = z2# - z1#
dist# = sqrt( (dx#^2)+(dz#^2) )
if dist# < 15.0
`player is close to the door, open it!
NDB_SetSliderMotorAccel DoorSlider, 50.0
`the door may have fallen asleep, so let's un-freeze it!"
if NDB_BodyActive(Door) = 0 then NDB_NewtonWorldUnfreezeBody Door
NEARDOOR = 1
else
if NEARDOOR = 1
`we have just walked away from being under the door,
`so make sure the door isn't frozen, or it won't shut!
if NDB_BodyActive(Door) = 0 then NDB_NewtonWorldUnfreezeBody Door
NEARDOOR = 0
endif
`player is away from door, turn motor off!
NDB_SetSliderMotorAccel DoorSlider, -50.0
endif
return
`all of these functions are pretty similar, they just make different collision
`primitives. I'll explain the BOX in detail, and the others are all basically the same,
`except for the Convex Hulls, which I'll explain in detail as well.
function MakeBox(x#,y#,z#,sx#,sy#,sz#,rx#,ry#,rz#,mass#)
Col = NDB_NewtonCreateBox(sx#, sy#, sz#)
Body = NDB_NewtonCreateBody(Col)
NDB_BuildMatrix rx#, ry#, rz#, x#, y#, z#
NDB_NewtonBodySetMatrix Body
NDB_CalculateMIBoxSolid mass#, sx#, sy#, sz#
NDB_NewtonBodySetMassMatrix Body, mass#
NDB_NewtonReleaseCollision Col
obj = FreeObject()
load object "..\media\box.x", obj
scale object obj, sx#*100.0, sy#*100.0, sz#*100.0
position object obj, x#, y#, z#
rotate object obj, rx#, ry#, rz#
color = GetColor()
color object obj, color
set object ambience obj, 50
NDB_BodySetDBProData Body, obj
NDB_NewtonBodySetDestructorCallback Body
NDB_BodySetGravity Body, 1
endfunction Body
function FreeObject()
repeat
inc i
if object exist(i)=0 then found=1
until found
endfunction i
function GetColor()
repeat
r = rnd(1)*255
g = rnd(1)*255
b = rnd(1)*255
until r<>0 or g<>0 or b<> 0
color = rgb(r,g,b)
endfunction color
`must force DBPro to include the memblock command set, because the wrapper uses these commands internally
`to make TreeCollision objects! it's also necessary for Convex Hull primitives as well.
function NeverCalled()
if memblock exist(1) then delete memblock 1
endfunction
Few code corections.There is no more puling toward the 0,0,0 coords.
Nik