2D Platformer Detection and scrolling
Author |
Message |
haayyeess
|
Posted: Sat Jan 16, 2016 4:35 pm Post subject: 2D Platformer Detection and scrolling |
|
|
What is it you are trying to achieve?
I'm creating a 2D platformer as a part of a project. Because of some poor design during the design process, it's become very challenging. The use of processes (not my idea) doesn't help.
What is the problem you are having?
- Currently the user can fall below ground and get stuck sometimes.
- They can also get stuck in air sometimes (ground level is set too high, even when it shouldn't be set) I believe this is because things are running at different speeds....
- I am not sure how to handle paging, or scrolling... Text file uses string values, which I can't make go into the negative for scrolling...
I'm willing to host someone through a Teamviewer meeting in order to look at the problems. It's hard to show and explain in a post.
Post any relevant code (You may choose to attach the file instead of posting the code if it is too long)
<Answer Here>
GRID DETECTION
Turing: |
%Grid_Load loads in the grid map to correspond with the running
%display, it will advance screens as required. Called by detection
%sends back grid coors to detection
%GRID LOAD
procedure Grid_Load (var GridMap : array 0 .. 36, 0 .. 36 of string (1))
%LOCAL VARIABLES
var FileNo : int %int cuz turing for loading in file
var FileName : string %name of file
%DECIDE WHICH FILE TO OPEN
case (CharLevel ) of
label 11 :
FileName := "Maps\\Level-1 Map.txt"
label 12 :
FileName := "Maps\\Level-12 Map.txt"
%FileName := "Maps\\Level-2 Map.txt"
label 13 :
%FileName := "Maps\\Level-3 Map.txt"
label 21 :
%FileName := "Maps\\Level-3 Map.txt"
label 22 :
%FileName := "Maps\\Level-3 Map.txt"
label 23 :
%FileName := "Maps\\Level-3 Map.txt"
label 31 :
%FileName := "Maps\\Level-3 Map.txt"
label 32 :
%FileName := "Maps\\Level-3 Map.txt"
label 33 :
%FileName := "Maps\\Level-3 Map.txt"
label 41 :
%FileName := "Maps\\Level-3 Map.txt"
label :
%Just cuz turing
end case
%LOAD MAP
open : FileNo, FileName, get
for decreasing y : 36 .. 0 % for every y
for x : 0 .. 36 %for every x
get : FileNo, GridMap (x, y ) %get the tile type at (x,y)
end for
end for
close (FileNo )
end Grid_Load
%Grid_Detection is a process used to detect possible collisions between the
% User's character and objects. Ideally Grid_Detection will check the player's
% future movement each space at a time to determine if the movement is doable.
%Grid_Detection relies on an external file known as a Level Render to know
% the (X,Y) coordinates of blocks, spikes and saves.
%GRID DETECTION
process Grid_Detection
%LOCAL VARIABLES
var GridMap : array 0 .. 36, 0 .. 36 of string (1)
%LOCAL VARIABLE ASSIGNMENTS
%GET A MAP IN HERE
Grid_Load (GridMap )
%Always has a level to work with when entering into main loop
%whether it's a brand new game, or something loaded by user
loop %MAIN DETECTION LOOP
%CHECKING FOR END OF SCREEN\LEVEL
if PlayerXLoc = 680 then %End of level
if natstr (CharLevel ) (*) = "3" then %End of map reached
CharLevel := CharLevel + 8 %Advance to next level
Grid_Load (GridMap )%Load new map
else %End of screen 1 or two, advance screen
CharLevel := CharLevel + 1
Grid_Load (GridMap ) %Load new map
end if
end if
%DETECTING OBJECTS USING GRID
%X-DIRECTION
%MOVING RIGHT * 20 to scale
if (GridMap (floor((PlayerXLoc + PLAYERXRAD + 20) / 20), ceil ((PlayerYLoc + PlayerYRad ) / 20)) = "B") or %Right Top
(GridMap (floor ((PlayerXLoc + PLAYERXRAD + 20) / 20), floor ((PlayerYLoc - PlayerYRad ) / 20)) = "B") then %Right Bottom
MovementFwdOK := false %Blocked
else
MovementFwdOK := true
end if
%MOVING LEFT * 20 to scale
if GridMap (ceil ((PlayerXLoc - PLAYERXRAD - 20) / 20), floor ((PlayerYLoc + PlayerYRad ) / 20)) = "B" or %Left Top
GridMap (ceil ((PlayerXLoc - PLAYERXRAD - 20) / 20), ceil ((PlayerYLoc - PlayerYRad ) / 20)) = "B" then %Left Bottom
MovementBackOK := false
else
MovementBackOK := true
end if
%Y-DIRECTION
%MOVING UP * 20 to scale
if GridMap (floor ((PlayerXLoc + PLAYERXRAD ) / 20), floor ((PlayerYLoc + 20 + PlayerYRad ) / 20)) = "B" or %First up Right
GridMap (ceil ((PlayerXLoc - PLAYERXRAD ) / 20), floor ((PlayerYLoc + 20 + PlayerYRad ) / 20)) = "B" or %First up Left
GridMap (floor ((PlayerXLoc + PLAYERXRAD ) / 20), floor ((PlayerYLoc + 40 + PlayerYRad ) / 20)) = "B" or %Second up Right
GridMap (ceil ((PlayerXLoc - PLAYERXRAD ) / 20), floor ((PlayerYLoc + 40 + PlayerYRad ) / 20)) = "B" then %Second up Left
MovementUpOK := false
%20 is the radius, removed from equation to avoid flicker when changing size
else
MovementUpOK := true
end if
%MOVING DOWN * 20 to scale
if GridMap (floor((PlayerXLoc + PLAYERXRAD ) / 20), ceil ((PlayerYLoc - 20 - PlayerYRad ) / 20)) = "B" or %down right
GridMap (ceil ((PlayerXLoc - PLAYERXRAD ) / 20), ceil ((PlayerYLoc - 20 - PlayerYRad ) / 20)) = "B" then %down left
OnGround := true %Can't go down assume on ground
GroundLevel := PlayerYLoc - PlayerYRad %Set ground level as the block below char to ensure stays above
else %Must be in air
OnGround := false
end if
%POLING LOOP
if PauseProcess then
loop
exit when not PauseProcess
end loop
end if
end loop %END OF MAIN DETECTION LOOP
end Grid_Detection
|
RENDER SCREEN
Turing: |
% Render_Screen put's everything possible to be displayed on the screen on
% the screen, and removes it as required. Render_Screen loads and draws
% images when needed, and relies highly on communications from
% User_Controls and Grid_Detection in the form of global variables and
% flags.
process Render_Screen
%LOCAL VARIABLES
var BackGround : int %Background for level one
var BGX : int % background pic x coor, int for turing
var BGY : int % background picture y coor, int for turing
%var Character : int
%Character := Pic.FileNew ("Graphics\\Game Graphics\\Character.jpg")
%testing below.....
var TestMap : array 0 .. 36, 0 .. 36 of string (1) %TEST
var FileNo : int %FOR TEST
var FileName : string %FOR TEST
%LOCAL VARIABLE ASSIGNMENTS
BGX := 0
BGY := 0
%below for test
FileName := "Maps\\Level-1 Map.txt"
%DECIDE BACKGROUND (off start)
if natstr (CharLevel ) (1) = "1" then
BackGround := Pic.FileNew ("Graphics\\Game Graphics\\Level-1.jpg")
elsif natstr (CharLevel ) (1) = "2" then
%Pic.Draw (Lvl2BG,0,0,0)
elsif natstr (CharLevel ) (1) = "3" then
%Pic.Draw (Lvl3BG,0,0,0)
else
%Pic.Draw (Lvl4BG,0,0,0)
%Draw boss map
end if
%SET SCREEN SIZE
View.Set ("Graphics:740;740,offscreenonly")
loop %MAIN DRAWING LOOP
%DRAW BACKGROUND
Pic.Draw (BackGround, BGX, BGY, 0)
%Last minute above ground check (here to prevent change)
if (PlayerYLoc - PlayerYRad ) < GroundLevel then
PlayerYLoc := GroundLevel + PlayerYRad
end if
%DRAW CHARACTER
Draw.FillOval (PlayerXLoc, PlayerYLoc, PLAYERXRAD, PlayerYRad, CharColor )
%Pic.Draw (Character, (PlayerXLoc), (PlayerYLoc), picMerge)
%x, y, xRadius, yRadius, Color : int
% %TEST
locate (1, 1)
put "X coor Game", PlayerXLoc
put "Y coor Game ", PlayerYLoc
put ""
put "Playr x vel ", PlayerXVel
put "Player y vel ", PlayerYVel
put ""
put "X coor Grid ", PlayerXLoc / 20
put "Y coor Grid ", PlayerYLoc / 20
put ""
put "up ok ", MovementUpOK
put "on ground ", OnGround
put "ground level ", GroundLevel
put "Y RAD ", PlayerYRad %WORKING 100%
%
View.Update %update
%IF USER IS AT END OF SCREEN
if PlayerXLoc = 680 then
%DECIDE BACKGROUND
if natstr (CharLevel ) (*) = "3" then %END OF LEVEL REACHED
if natstr (CharLevel ) (1) = "1" then
BackGround := Pic.FileNew ("Graphics\\Game Graphics\\Level-1.jpg")
Pic.Draw (BackGround, 0, 0, 0)
elsif natstr (CharLevel ) (1) = "2" then
%Pic.Draw (Lvl2BG,0,0,0)
elsif natstr (CharLevel ) (1) = "3" then
%Pic.Draw (Lvl3BG,0,0,0)
else
%Pic.Draw (Lvl4BG,0,0,0)
%Draw boss map
end if
BGX := 0 %Reassign to start of picture cuz new level
else %USER IS NOT AT END OF LEVEL, ADVANCE SCREEN
BGX := BGX + 740 %SHIFT BG PICTURE TO LEFT (NEW SCREEN)
end if
end if
end loop %END MAIN DRAWING LOOP
end Render_Screen
|
USER CONTROLS
Turing: |
%PLEASE SEE THE MAIN MENU FOR MORE INFORMATION INCLUDING GLOBAL VALUES
%#%#%#%%#%#%#%#%%#%#%#%#%%#%#% USER CONTROLS #%#%%#%#%#%#%#%#%#%#%%#%#%#%#%#
/*User Controls is the subprogram responsible for all of the in-game action
requiring user input. User_Controls is constantly checking for user input and
storing entered keys in order to see if they control a function. Any key
without a function is ignored. Based on the entered keys, User_Controls
follows through with an action of character movement, or entering the
Pause_Menu. User_Controls updates global variables as required to notify
processes in gameplay of required action.
*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%
%*% USER CONTROLS CONSTANT AND VARIABLE LIST %*%
%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*
$$$$$$$$$$$$$$$$$$$$$$$$$ CONSTANTS $$$$$$$$$$$$$$$$$$$$$$$$$
%LOCAL
JUMPSPEED: Value regulating the speed and height a user can jump in Y
direction per key.
RUNSPEED: Value regulating the speed and distance a user moves in the X
direction per key.
%GLOBAL
GRAVITY: Value regulating how fast a player's Y velocity and height is
returned to 0 after jump.
PLAYERXRAD: Stores the player's radius value for the X direction.
$$$$$$$$$$$$$$$$$$$$$$$$$ VARIABLES $$$$$$$$$$$$$$$$$$$$$$$$$
%LOCAL
Chars: ARRAY CHAR OF BOOLEAN, stores pressed keys and used for
keyboard movement.
%GLOBAL
StopProcess: Boolean flag set when a user exits from within in-game play,
initially false.
PauseProcess: Boolean flag set when a user enters the pause menu,
initially false.
MovementUpOK: Boolean flag set when a user's next possible upward position
will not interfere with object.
MovementFwdOK: Boolean flag set when a user's next possible forward position
will not interfere with object.
MovementBackOK: Boolean flag set when a user's next possible backward position
will not interfere with object.
OnGround: Boolean flag set when the user is on the ground level,
set initially true.
PlayerXLoc: NAT 2 variable storing the user's X location upon saving
their last game.
PlayerYLoc: NAT 2 variable storing the user's Y location upon saving
their last game.
PlayerXVel: REAL 4 variable storing the user's X direction velocity,
or speed.
PlayerYVel: REAL 4 variable storing the user's Y direction velocity,
or speed.
PlayerYRad: NAT 1 variable storing the player's radius value for
the Y direction.
GroundLevel: NAT 2 variable storing the ground level.
*/
%SUBPROGRAMS
%PAUSE_MENU
include "Pause_Menu.t"
procedure User_Controls
%LOCAL CONSTANTS
const RUNSPEED := 20 %Numeric, controls speed (step) of users run (X-Dir)
const JUMPSPEED := 40 %Numeric, controls speed (step) of users jump (Y-Dir)
%LOCAL VARIABLES
var Chars : array char of boolean %Allows user to make choices throughout prog
var ShiftChar, ShiftChar2 : boolean %For shifting character ONCE after or when down is prsd
var NoAccumGravity : boolean %Flag for preventing the accumulation of
%gravity to prevent the bullet through paper effect of too fast movmnt
%LOCAL VARIABLE ASSIGNMENTS
OnGround := true %dropping user from air
ShiftChar := false %not yet
ShiftChar2 := false
NoAccumGravity := true
loop
Input.KeyDown (Chars ) %Allow user input, track for action
%PAUSE CHECK
if Chars (' ') then %Spacebar was pressed
PauseProcess := true %Turn pause flag ON
Pause_Menu %Call Pause
end if
%X DIRECTION, (LEFT-RIGHT) MOVEMENT CHECK
if Chars (KEY_RIGHT_ARROW) and MovementFwdOK then
PlayerXVel := RUNSPEED % Assign X velocity pos. runspeed
elsif Chars (KEY_LEFT_ARROW) and MovementBackOK then
PlayerXVel := -RUNSPEED % Assign X velocity neg. runspeed
else
PlayerXVel := 0 % Assign X velocity 0, stop x-dir movement
%will change if add sloped rolling, possible v2.
end if
%GRAVITY EFFECT
if not OnGround then
PlayerYVel := (PlayerYVel - GRAVITY )
%prevent from getting too fast
% if PlayerYVel < -40 then
% PlayerYVel := -40
% end if
else
PlayerYVel: = 0
end if
%Y DIRECTION, (UP-DOWN) MOVEMENT CHECK
if Chars (KEY_UP_ARROW) and MovementUpOK and OnGround then
PlayerYVel := JUMPSPEED
elsif Chars (KEY_DOWN_ARROW) and OnGround then
PlayerYRad := round (0. 5 * PLAYERXRAD ) %will always be whole
ShiftChar := true
if ShiftChar2 then
PlayerYLoc := PlayerYLoc + round (PLAYERXRAD )
ShiftChar2 := false
end if
%
elsif not Chars (KEY_DOWN_ARROW) and MovementUpOK and ShiftChar then
PlayerYRad := PLAYERXRAD
PlayerYLoc := PlayerYLoc + round (0. 5 * PLAYERXRAD )
ShiftChar := false
ShiftChar2 := true
end if
%DO MOVEMENT
PlayerXLoc := PlayerXLoc + ceil (PlayerXVel )
PlayerYLoc := PlayerYLoc + ceil (PlayerYVel )
%CHECK FOR EXIT
exit when StopProcess
delay (50) %to slow things down a wee bit and regulate speed
end loop
end User_Controls
|
|
|
|
|
|
|
Sponsor Sponsor
|
|
|
Insectoid
|
Posted: Sat Jan 16, 2016 5:12 pm Post subject: RE:2D Platformer Detection and scrolling |
|
|
You're going to want to completely remove processes from your program. They are going to (and likely already have) cause you a ton of headaches, especially where multiple processes access the same variables (which your program does).
Remember, processes do not (generally) run simultaneously. They take turns. Process A runs for a few milliseconds, then process B takes over. Process A can't trust variables from process B and vice versa, because neither process has any idea how far the other is in its execution.
For example, your variable 'movementfwdok' is used by both grid_detection and user_controls. user_controls checks this variable to determine if the player can move. What if user_controls completes two frames before grid_detection finishes calculating movefwdok? Movefwdok will be out of date, and your character might be able to move through a wall, or be blocked where there is no wall.
This will cause extreme issues where a process pauses halfway to let another take over and a lot of variables are shared. a process may be halfway done calculating a new frame, and then pause. The next process that uses those variables will have only half the data from the current frame, while the other half is from the previous frame, so the process will give completely incorrect results.
Long processes may pause several times before completing, so processes depending on them will have a mix of data from several frames, which will seriously fragment your program and even crash it. I'm sure by now you realize how massive your problem is.
I'm afraid there is no way to salvage your current work. It's not a quick fix. You'll need to completely reorganize it to eliminate processes from your code. Most of actual lines can be saved, but will have to be totally rearranged. Make sure you are familiar with the fundamentals of a game loop before carry this out. |
|
|
|
|
|
haayyeess
|
Posted: Sat Jan 16, 2016 5:42 pm Post subject: Re: 2D Platformer Detection and scrolling |
|
|
Thank you Insectoid. Could you please example what you mean about the "fundamentals of a game loop"? |
|
|
|
|
|
Insectoid
|
Posted: Sat Jan 16, 2016 5:55 pm Post subject: RE:2D Platformer Detection and scrolling |
|
|
Every successful game in Turing looks more or less the same. Everything takes place sequentially inside one big loop. You shouldn't have any other loops in there unless you know what you're doing. The basic structure is as follows:
code: | loop
Input.KeyDown
%Update everything by one frame.
%Draw everything
View.Update
delay
cls
end loop |
The important bit here is 'update everything by one frame'. One frame. I'm repeating myself, but there should be no loops in here unless you know what you're doing. Here is where you deal with user input, check for collisions and respond to them, update the AI and the player's scores, whatever. Anything that changes, changes here, and it only changes by one frame.
If you stick to this template, your game will run smoothly and your code will be easy to read. Bugs will be generally obvious and everything is predicable. |
|
|
|
|
|
haayyeess
|
Posted: Sat Jan 16, 2016 6:18 pm Post subject: Re: RE:2D Platformer Detection and scrolling |
|
|
Insectoid @ Sat Jan 16, 2016 5:55 pm wrote: Every successful game in Turing looks more or less the same. Everything takes place sequentially inside one big loop. You shouldn't have any other loops in there unless you know what you're doing. The basic structure is as follows:
code: | loop
Input.KeyDown
%Update everything by one frame.
%Draw everything
View.Update
delay
cls
end loop |
The important bit here is 'update everything by one frame'. One frame. I'm repeating myself, but there should be no loops in here unless you know what you're doing. Here is where you deal with user input, check for collisions and respond to them, update the AI and the player's scores, whatever. Anything that changes, changes here, and it only changes by one frame.
If you stick to this template, your game will run smoothly and your code will be easy to read. Bugs will be generally obvious and everything is predicable.
Thank you so much. Huge code roll over here I come. |
|
|
|
|
|
|
|