Ahhh --- Ok - I took some major liberties in structuring your code for a few reasons.
1: Readability - though your code wasn't ugly - just a little cryptic due to hardcoded object id's
2: Gosub Crazy. Gosub is not really the "way to go" - typiccally functions are better - however dbpro has a few quirks on how you have to init types and (I think also ) constants outside of a function. so Gosub is good for making that happen without needing all that init stuff at the very top of your file.
3: Utilized some code I wrote for polling input - some of it borrowed from here some not - makes reading the code concerning input easier - not more KEYSTATE(30) - Now: KeyState(cnScanCode_A)
4: Separated user input form actions. Not the way I do it - but almost. This is a little simpler to read than what I do. This should make some sense though when you see the logic for how to move the guy - the code doesn't read "if this key then rotate" ... it reads more like "grPlayer.bGoLeft = keystate(cnScanCode_W)"
then in the "PlayerActions" it takes all the keypresses into account to decide how to carry on.
5: Made MOST of your hardcoded numbers dynamic - for object player anyway - I gave you code that allows you to get "ID numbers" the same way for all "things" in DBPro - like objects, terrain, matrix, images etc.
Well - here is the code. I think you will be quite pleased.
BTW - This wasn't a half hour of work - more like 3-4
Went from 156 lines of code to 671
There are quite a few spacer lines - but you'll see. I also tried to comment liberally for your sake - learning from etc.
`------------------------------------------------------------------------------------------
` Author: Existance
` Greets: "Sage"
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
` Prepare for Battle
`------------------------------------------------------------------------------------------
GOSUB InitializeApplication
PrepareDisplay()
LoadMedia()
PlayerReset()
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
Rem Main Loop
`------------------------------------------------------------------------------------------
Do
PollUserInput()
PlayerActions()
AI()
Collision()
Debug()
UpdateWorld()
CONTROL CAMERA USING ARROWKEYS 0,3,2
SYNC
UpdateTime()
LOOP
END
`------------------------------------------------------------------------------------------
InitializeApplication:
` Important for easier to read code.
` remember a complex game requires COMPLEX (LOTS) of code!
` Readability jockeys for position with "gamespeed"
` because the best code in the world is kind stuck if you can;t
` go back a month later and fix/modify/enhance it easily.
#CONSTANT TRUE=1
#CONSTANT FALSE = 0
` Also GOSUB (in dbpro) is best for things that like initializing
` constants and types, not routines so much because generally speaking
` GOSUB is an "old school" command that is not normally recommended at all.
` You can usually accomplish the same thing via functions. A reason that
` functions are more pliable is because when you use a non-global variable
` in them - its like they don't exist to the rest of the application.
` In fact - each time you call the function - its like those variables
` never existed.
`---------------------------------------------------------------------
` Related to GetNewID() Function
`---------------------------------------------------------------------
#CONSTANT cnObject =1
#CONSTANT cnCamera =2
#CONSTANT cnMatrix =3
#CONSTANT cnBitmap =4
#CONSTANT cnImage =5
#CONSTANT cnLight =6
#CONSTANT cnMusic =7
#CONSTANT cnAnimation =8
#CONSTANT cnSound =9
#CONSTANT cnMemBlock = 10
#CONSTANT cnTerrain=11
` Keyboard ScanCodes
#CONSTANT cnScanCode_A=30
#CONSTANT cnScanCode_B=48
#CONSTANT cnScanCode_C=46
#CONSTANT cnScanCode_D=32
#CONSTANT cnScanCode_E=18
#CONSTANT cnScanCode_F=33
#CONSTANT cnScanCode_G=34
#CONSTANT cnScanCode_H=35
#CONSTANT cnScanCode_I=23
#CONSTANT cnScanCode_J=36
#CONSTANT cnScanCode_K=37
#CONSTANT cnScanCode_L=38
#CONSTANT cnScanCode_M=50
#CONSTANT cnScanCode_N=49
#CONSTANT cnScanCode_O=24
#CONSTANT cnScanCode_P=25
#CONSTANT cnScanCode_Q=16
#CONSTANT cnScanCode_R=19
#CONSTANT cnScanCode_S=31
#CONSTANT cnScanCode_T=20
#CONSTANT cnScanCode_U=22
#CONSTANT cnScanCode_V=47
#CONSTANT cnScanCode_W=17
#CONSTANT cnScanCode_X=45
#CONSTANT cnScanCode_Y=21
#CONSTANT cnScanCode_Z=44
#CONSTANT cnScanCode_F1=59
#CONSTANT cnScanCode_F2=60
#CONSTANT cnScanCode_F3=61
#CONSTANT cnScanCode_F4=62
#CONSTANT cnScanCode_F5=63
#CONSTANT cnScanCode_F6=64
#CONSTANT cnScanCode_F7=65
#CONSTANT cnScanCode_F8=66
#CONSTANT cnScanCode_F9=67
#CONSTANT cnScanCode_F10=68
#CONSTANT cnScanCode_LeftSingleQuote=41
#CONSTANT cnScanCode_1=2
#CONSTANT cnScanCode_2=3
#CONSTANT cnScanCode_3=4
#CONSTANT cnScanCode_4=5
#CONSTANT cnScanCode_5=6
#CONSTANT cnScanCode_6=7
#CONSTANT cnScanCode_7=8
#CONSTANT cnScanCode_8=9
#CONSTANT cnScanCode_9=10
#CONSTANT cnScanCode_0=11
#CONSTANT cnScanCode_Minus=12
#CONSTANT cnScanCode_Equal=13
#CONSTANT cnScanCode_BackSpace=14
#CONSTANT cnScanCode_Tab=15
#CONSTANT cnScanCode_LeftBracket=26
#CONSTANT cnScanCode_RightBracket=27
#CONSTANT cnScanCode_BackSlash=43
#CONSTANT cnScanCode_Enter=28
#CONSTANT cnScanCode_Caps=58
#CONSTANT cnScanCode_LShift=42
#CONSTANT cnScanCode_RShift=54
#CONSTANT cnScanCode_SemiColon=39
#CONSTANT cnScanCode_SingleQuote=40
#CONSTANT cnScanCode_Comma=51
#CONSTANT cnScanCode_Period=52
#CONSTANT cnScanCode_ForwardSlash=53
#CONSTANT cncanCode_LWinKey=219
#CONSTANT cnScanCode_LAlt=56
#CONSTANT cnScanCode_RAlt=184
#CONSTANT cnScanCode_RWinKey=220
#CONSTANT cnScanCode_WinMenu=221
#CONSTANT cnScanCode_LCntl=29
#CONSTANT cnScanCode_RCntl=157
#CONSTANT cnScanCode_PrintScreen=183
#CONSTANT cnScanCode_ScollLock=70
#CONSTANT cnScanCode_Pause=197
#CONSTANT cnScanCode_Ins=210
#CONSTANT cnScanCode_Del=211
#CONSTANT cnScanCode_Home=199
#CONSTANT cnScanCode_End=207
#CONSTANT cnScanCode_PgUp=201
#CONSTANT cnScanCode_PgDn=209
#CONSTANT cnScanCode_UpArrow=200
#CONSTANT cnScanCode_DownArrow=208
#CONSTANT cnScanCode_LeftArrow=203
#CONSTANT cnScanCode_RightArrow=205
#CONSTANT cnScanCode_NumLock=69
#CONSTANT cnScanCode_NumForwardSlash=181
#CONSTANT cnScanCode_NumStar=55
#CONSTANT cnScanCode_NumMinus=74
#CONSTANT cnScanCode_NumPlus=78
#CONSTANT cnScanCode_NumEnter=156
#CONSTANT cnScanCode_NumPeriod=83
#CONSTANT cnScanCode_Num1=79
#CONSTANT cnScanCode_Num2=80
#CONSTANT cnScanCode_Num3=81
#CONSTANT cnScanCode_Num4=75
#CONSTANT cnScanCode_Num5=76
#CONSTANT cnScanCode_Num6=77
#CONSTANT cnScanCode_Num7=71
#CONSTANT cnScanCode_Num8=72
#CONSTANT cnScanCode_Num9=73
#CONSTANT cnScanCode_Num0=82
#CONSTANT cnScanCode_SpaceBar = 57
` Configure globals in a type so less likely to TYPE them wrong when editing code
` Use name rtGlobal because thie is a Record Type of Globals
TYPE rtGlobals
iTimer as integer
iTimerLast as integer
iTimerElapsed as Integer
objLevel as integer
ENDTYPE
` Use name "G" for shorthand for grGlobal or "Global Record Global"
GLOBAL G as rtGlobals
` Avoid the Zero problem of timer starting (Midmight COULD cause a bug when timer goes back to zero)
` We are priming the timer "pump"
g.iTimerLast = g.iTimer-1
g.iTimer = Timer()
TYPE rtPlayer
obj as integer
iHealth as integer
bAttacking as boolean
bWalking as Boolean
bIdle as boolean
`--------------------
` I'm using this as my "player Actions Input polling
` So its all in the same "record" or "type"
bGoForward as boolean
bGoBackward as boolean
bGoLeft as boolean
bGoRight as boolean
bGoAttack as boolean
ENDTYPE
GLOBAL grPlayer as rtPlayer
return
`------------------------------------------------------------------------------------------
` -----------------------------------------------------------------------------------------------------------------------
` Note this works great but I could see it slowing down If you had ALOT of objects of a particular Type
` and trying to Add another of the Type. That's what the GetNewID_StartAt function is all about. If you KNOW,
` For example you have a 1000 items in a scene, and Now need to be able to Add and Remove the bad guys
` QUICKLY during the game, use this routine - and specify a STARTHERE value that is greater than your highest object,
` or just pick a number out in Left field. Alternatively, If you wanted to reserve #'s 1-100, say for bad guy OBJECTs,
` Call all your other creation like: GetNewID_StartAt(cnObject,101) - so your reserved 1-100 is Not used.
Function GetNewID(p_iType)
` -----------------------------------------------------------------------------------------------------------------------
iNewId = GetNewID_StartAt(p_iType, 1)
ENDFUNCTION iNewID
` -----------------------------------------------------------------------------------------------------------------------
` -----------------------------------------------------------------------------------------------------------------------
` Same As GetNewID(p_itype) With control of WHERE the Function starts seeking out an am empty slot.
` NOTE: To Wrap Around Logic is in Place. Searching Stops If a free "thing" is Not available by the Time the
` counter (iSeeker) hits 65535. (Hardcoded)
Function GetNewID_StartAt(p_iType, p_iStartHere)
` -----------------------------------------------------------------------------------------------------------------------
iSeeker=p_iStartHere
iNewID =0
Select p_itype
Case cnObject:
repeat
inc iSeeker
If OBJECT EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnBitmap:
repeat
inc iSeeker
If bitmap EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnCamera:
repeat
inc iSeeker
If (camera EXIST(iSeeker)=0) Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnTerrain:
repeat
inc iSeeker
If Terrain EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnSound:
repeat
inc iSeeker
If sound EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnMusic:
repeat
inc iSeeker
If music EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnImage:
repeat
inc iSeeker
If image EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnLight:
repeat
inc iSeeker
If light EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnAnimation:
repeat
inc iSeeker
If animation EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnMemblock:
repeat
inc iSeeker
If memblock EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
Case cnMatrix:
repeat
inc iSeeker
If matrix EXIST(iSeeker)=0 Then iNewID=iSeeker
until (iNewID<>0) or (iSeeker>=65535)
endcase
endselect
ENDFUNCTION iNewID
` -----------------------------------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
Function PrepareDisplay()
`------------------------------------------------------------------------------------------
SYNC On : Hide MOUSE
Rem AA
LOAD DLL "user32.dll",1
Sw=Call DLL(1,"GetSystemMetrics",0)
Sh=Call DLL(1,"GetSystemMetrics",1)
Delete DLL 1
Set DISPLAY MODE Sw,Sh,32,0
CLS
SET CURSOR 1,1
Print "Loading Media..."
sync
ENDFUNCTION
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
Rem Handle loading of objects and textures
`------------------------------------------------------------------------------------------
function LoadMedia()
`------------------------------------------------------------------------------------------
Rem Our Player's object
grPlayer.obj = GetNewID(cnObject)
LOAD OBJECT "MediaKnightH-Knight-Idle.x",grPlayer.obj
Rem Turn off autocam
AUTOCAM OFF
Rem Position the object
POSITION OBJECT grPlayer.obj,-3,-98,-550
Rem This guy is way too small
Scale OBJECT grPlayer.obj,5000,5000,5000
Rem Time to rotate the player's object around so that you can opnly see his back
YROTATE OBJECT grPlayer.obj,180
Rem Append Walking animation to the first
Append OBJECT "MediaKnightH-Knight-Attack1.x",grPlayer.obj,25
Rem Append Attacking animation to the first
Append OBJECT "MediaKnightH-Knight-Move.x",grPlayer.obj,50
Rem Set the animations' play speed
Set OBJECT SPEED grPlayer.obj,30
Rem Load the Room you will play in
g.objLevel = GetNewID(cnObject)
LOAD OBJECT "Mediadungeongame gamewrold.x",g.objLevel
Rem Load the texture for the gameworld
` Img is temporary because we are in a function. Its not declared anywhere
` else as a global so we are good. Identifying globals versus local variables
` is important and is why I have a "g" in front of my globals' names.
img = GetNewID(cnImage)
LOAD IMAGE "Mediawall_U3_09.jpg",img
Rem Load the impact animation to add to the first
Append OBJECT "MediaKnightH-Knight-Impact.x",grPlayer.obj,75
Rem Make PLayer's object specular
Set OBJECT SPECULAR grPlayer.obj,RGB(255,0,0)
Rem Make him real shiny
Set OBJECT SPECULAR POWER grPlayer.obj,2000
Rem Texture the world with this image
TEXTURE OBJECT g.objLevel,img
Rem Position the Camera
POSITION CAMERA 2,2,2
Rem Make it dark
Set AMBIENT LIGHT 0
Rem Set shadows on :)
Set SHADOW SHADING On grPlayer.obj,-1,500,1
` Do This later
`````Rem Loop the Idle animation
`````LOOP OBJECT 1,0,24
Rem Addin another light
MAKE LIGHT 1
COLOR LIGHT 1,RGB(255,0,0)
POSITION LIGHT 1,-3,-98,-520
Rem And another...
MAKE LIGHT 2
COLOR LIGHT 2,RGB(255,255,255)
Rem another...
MAKE LIGHT 3
COLOR LIGHT 3,RGB(0,255,150)
POSITION LIGHT 3,-3,-98,-200
Rem Show where light is
` These hardcoded object numbers are not the best idea.
` Try to implement the GetNewID function I tossed ya.
` Dynamic is better - assign to variable name that makes sense
` to make code easier to read PLUS never worry what number
` you used for what again. When you need a range of object numbers for
` looping purposes - see the GetNewID_StartAt() function I gave ya
` so you can allocate "fomr this # to that #" say for enemies, then
` one loop can handle all of them easier... same as using object #1-10 for
` bad guys.
MAKE OBJECT BOX 90,10,10,10
POSITION OBJECT 90,LIGHT POSITION X(2),LIGHT POSITION Y(2),LIGHT POSITION Z(2)
MAKE OBJECT BOX 91,10,10,10
POSITION OBJECT 91,LIGHT POSITION Z(1),LIGHT POSITION Y(1),LIGHT POSITION Z(1)
MAKE OBJECT BOX 92,10,10,10
POSITION OBJECT 92,LIGHT POSITION X(3),LIGHT POSITION Y(3), LIGHT POSITION Z(3)
Rem Mess around with lighting
Hide LIGHT 0
Rem Add some fog, to make it eerie
FOG On
FOG COLOR RGB(0,0,0)
Rem Make limbs for collision
ENDFUNCTION
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function PlayerIdle()
`------------------------------------------------------------------------------------------
Rem Loop the Idle animation
LOOP OBJECT grPlayer.obj,0,24
endfunction
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function PlayerAttack()
`------------------------------------------------------------------------------------------
play object grPlayer.obj,25,49
endfunction
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function PlayerWalking()
`------------------------------------------------------------------------------------------
LOOP OBJECT grPlayer.obj,55,75
endfunction
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function PlayerReset()
grPlayer.iHealth=100
grPlayer.bAttacking = FALSE
grPlayer.bWalking = FALSE
grPlayer.bIdle = true
PlayerIdle()
endfunction
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function PollUserInput()
`------------------------------------------------------------------------------------------
grPlayer.bGoForward = keystate(cnScanCode_W)
grPlayer.bGoBackward = keystate(cnScanCode_S)
grPlayer.bGoLeft = keystate(cnScanCode_A)
grPlayer.bGoRight = keystate(cnScanCode_D)
iMouseclick=MouseClick()
grPlayer.bGoAttack = ((iMouseClick and 1)=1)
` Sample button 2: ((iMouseClick and 2)=2)
` Sample button 3: ((iMouseClick and 4)=4)
` Sample button 4: ((iMouseClick and 8)=8)
` You put the whole thing in paren so that if TRUE the result that goes
` in the Boolean (such as bGoAttack becomes 0 or 1 .. or TRUE or FALSE
` Note How we only polled the hardware for each desired thing ONCE
` Now we have the info - we use the variables - not multiple calls to KEYSTATE
` to ask the same question again. Could throw off logic if value changes from one KEYSTATE
` check to the next. Here we "snapshot" what the user was doing for this pass through
` the loop and we run with it.
` In the player Actions we will REACT to it and work in the attack, the walking etc.
endfunction
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function PlayerActions()
` Reset animations Appropriately
` Logic dictates in this example that ATTACKING supercedes everything, then walking,
` then idle.
bSomethingHappening = FALSE
` turn off attacking if attacking but the animation is done
if grPlayer.bAttacking
if not object playing (grPlayer.obj)
grPlayer.bAttacking = false
else
bSomethingHappening=true
endif
endif
` User want to be Attacking?
if (grPlayer.bGoAttack)
bSomethingHappening = true
` Are we already Attacking?
text 200,200,"ATTACK"
if not grPlayer.bAttacking
grPlayer.bIdle = False
grPlayer.bWalking = false
grPlayer.bAttacking = true
PlayerAttack()
endif
endif
` Now lets see if we are walking Forward
if (grPlayer.bGoForward) or (grPlayer.bGoBackward) or (grPlayer.bGoLeft) or (grPlayer.bGoRight)
` Let's allow guy to still move WHILE Attacking
bSomethingHappening=true
if (not grPlayer.bAttacking)
grPlayer.bIdle = false
grPlayer.bWalking=true
PlayerWalking()
endif
if (grPlayer.bGoForward)
Move OBJECT grPlayer.obj,-1
endif
if (grPlayer.bGoBackward)
Move OBJECT grPlayer.obj,1
endif
if (grPlayer.bGoLefT)
YROTATE OBJECT grPlayer.obj,WRAPVALUE(OBJECT ANGLE Y(grPlayer.obj)-1)
endif
if (grPlayer.bGoRight)
YROTATE OBJECT grPlayer.obj,WRAPVALUE(OBJECT ANGLE Y(grPlayer.obj)+1)
endif
endif
if (not bSomethingHappening)
if not grPlayer.bIdle
grPlayer.bIdle=true
PlayerIdle()
endif
grPlayer.bAttacking = false
grPlayer.bWalking = false
endif
ENDFUNCTION
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function UpdateWorld()
`------------------------------------------------------------------------------------------
Rem First light...(eerie red 1)
Set LIGHT RANGE 1,Rnd(400)+600
Rem 2nd(plyr's torch)
Set LIGHT RANGE 1,200
Rem POSITION 2ND LIGHT TO PLAYER(TORCH)
POSITION LIGHT 2,OBJECT POSITION X(grPlayer.obj),OBJECT POSITION Y(grPlayer.obj)+5,OBJECT POSITION Z(grPlayer.obj)+5
TEXT 0,50,"PLAYER'S TORCH RANGE:"+Str$(LIGHT RANGE(2))
Rem Next light...
Set LIGHT RANGE 3,(400)+600
ENDFUNCTION
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function Debug()
`------------------------------------------------------------------------------------------
plyrx=OBJECT POSITION X(grPlayer.obj)
plyry=OBJECT POSITION Y(grPlayer.obj)
plyrz=OBJECT POSITION Z(grPlayer.obj)
TEXT 0,0,"FPS:"+Str$(SCREEN FPS())
TEXT 0,10,"Polys:"+Str$(STATISTIC(1))
TEXT 0,20,"PLAYER POSITION X:"+Str$(plyrx)
TEXT 0,30,"PLAYER POSITION Y:"+Str$(plyry)
TEXT 0,40,"PLAYER POSITION Z"+Str$(plyrz)
TEXT 0,150,"grPlayer.bIdle:"+Str$(grPlayer.bIdle)
TEXT 0,160,"grPlayer.bWalking:"+Str$(grPlayer.bWalking)
TEXT 0,170,"grPlayer.bAttacking:"+Str$(grPlayer.bAttacking)
TEXT 0,180,"grPlayer.bGoForward:"+Str$(grPlayer.bGoForward)
TEXT 0,190,"grPlayer.bGoBackward:"+Str$(grPlayer.bGoBackward)
TEXT 0,200,"grPlayer.bGoLeft:"+Str$(grPlayer.bGoLeft)
TEXT 0,210,"grPlayer.bGoRight:"+Str$(grPlayer.bGoRight)
TEXT 0,220,"grPlayer.bGoAttack:"+Str$(grPlayer.bGoAttack)
CENTER TEXT OBJECT SCREEN X(91),OBJECT SCREEN Y(91)-70,"LIGHT1"
CENTER TEXT OBJECT SCREEN X(90),OBJECT SCREEN Y(90)-70,"LIGHT2"
CENTER TEXT OBJECT SCREEN X(92),OBJECT SCREEN Y(92)-70,"LIGHT3"
endfunction
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
function UpdateTime()
`------------------------------------------------------------------------------------------
g.iTimerLast = g.iTimer
g.iTimer = Timer()
g.iTimerElapsed = g.iTimer - g.iTimerLast
endfunction
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
Rem Handle the Artificial Intelligience
`------------------------------------------------------------------------------------------
function AI()
`------------------------------------------------------------------------------------------
endfunction
`------------------------------------------------------------------------------------------
`------------------------------------------------------------------------------------------
Rem Handle Collision
`------------------------------------------------------------------------------------------
function Collision()
`------------------------------------------------------------------------------------------
endfunction
`------------------------------------------------------------------------------------------
Best Regards Bro - Have Fun
Jason p "Sage"
Know way too many languages - Master of none