well, if it'll help any, here's some code from my fps i was workin on (with newton). basically, i only calculated the acceleration when the player wanted to move, so the slowing down of the player all happened by friction. in the way it's currently done with blackout, the player is being slowed down by negative acceleration. the reason walaber did this might be because of stairs though.
if (main.move_fb<>0 or main.move_lr<>0) and playernotanimating(num)
if player(num).crouched
if sound playing(player(num).crawl_f.sound)=0 then play sound player(num).crawl_f.sound
inc player(num).vel.x,player(num).caccel*(sin(direction)*player(num).cspeed-player(num).vel.x)*main.rate
inc player(num).vel.z,player(num).caccel*(cos(direction)*player(num).cspeed-player(num).vel.z)*main.rate
set vector3 1,player(num).vel.x,0,player(num).vel.z
weapon(num,player(num).currentweapon).bob.x=wrapvalue(weapon(num,player(num).currentweapon).bob.x+player(num).weaponbobspeed*(length vector3(1)/player(num).sspeed))
weapon(num,player(num).currentweapon).bob.y=wrapvalue(weapon(num,player(num).currentweapon).bob.y+player(num).weaponbobspeed*(length vector3(1)/player(num).sspeed)*2)
else
if sound playing(player(num).walk_f.sound)=0 then play sound player(num).walk_f.sound
inc player(num).vel.x,player(num).saccel*(sin(direction)*player(num).sspeed-player(num).vel.x)*main.rate
inc player(num).vel.z,player(num).saccel*(cos(direction)*player(num).sspeed-player(num).vel.z)*main.rate
set vector3 1,player(num).vel.x,0,player(num).vel.z
weapon(num,player(num).currentweapon).bob.x=wrapvalue(weapon(num,player(num).currentweapon).bob.x+player(num).weaponbobspeed*(length vector3(1)/player(num).sspeed))
weapon(num,player(num).currentweapon).bob.y=wrapvalue(weapon(num,player(num).currentweapon).bob.y+player(num).weaponbobspeed*(length vector3(1)/player(num).sspeed)*2)
endif
else
edit:
here's that same principal in action with the fps demo walaber made. just replace all of walaber's demo code with this, and run it.
`--------------------------------------------------
` NEWTON PHYSICS SDK WRAPPER
` DEMO PROJECT 5 - first person shooter example
`
`--------------------------------------------------
null=make vector3(1)
`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# = 20.0
global Player_Accel# = 0.05
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
`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\room.dbo", obj
`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, -3.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.4, 0.4
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
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
x#=NDB_GetVector_X()
y#=NDB_GetVector_Y()
z#=NDB_GetVector_Z()
position camera x#,y#,z#
ndb_buildmatrix 0,Player_Yangle#,0,x#,y#,z#
ndb_newtonbodysetmatrix Player
`get current vel
ndb_newtonbodygetvelocity Player
vx#=ndb_getvector_x(1)
vy#=ndb_getvector_y(1)
vz#=ndb_getvector_z(1)
rem accelerate
if keystate(17)
ax#=Player_Accel#*((Player_Speed#*keystate(17)*sin(Player_Yangle#))-vx#)
az#=Player_Accel#*((Player_Speed#*keystate(17)*cos(Player_Yangle#))-vz#)
ndb_setvector 1,vx#+ax#,vy#,vz#+az#
endif
ndb_newtonbodysetvelocity 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
formerly xMik
