Computer Science Canada

Jumping

Author:  Flea [ Wed Oct 27, 2004 8:31 pm ]
Post subject:  Jumping

Hi, I'm making a game. I;'ve already got about 650 essential lines of code just for the menu and character saving/loading and customization. anyway, i started the actual "doing stuff" part of the program. its a 2-d megaman/castlevania style. i try not to ask too many questions cause most stuff can be figured out without wasting everyones time Laughing

anyway heres a part of my basic code i whipped up in a minute for "gravity" and walking lol:

code:
loop
    Input.KeyDown (chars)
    if chars (KEY_RIGHT_ARROW) then
        mage1x := mage1x + 1
        boo := false
    elsif chars (KEY_LEFT_ARROW) then
        mage1x := mage1x - 1
        boo := true
    end if
    if whatdotcolour (mage1x, mage1y - 5) = 7 then
        mage1y := mage1y - 1
    end if
    if boo = true then
        Pic.Draw (mage1, mage1x, mage1y, picCopy)
    else
        Pic.Draw (mage2, mage1x, mage1y, picCopy)
    end if
    delay (10)
end loop


I cant figure out jumping though. I tried doing a little

code:
    if jumpcount > 10 then
        jumpcount:= jumpcount + 1
        mage1y := mage1y + 2
    else
        jumpcount:= 0
        mage1y := mage1y - 10
    end if


but thats not working and ive tried it many differnt ways within the above loop. Confused i apologize if you dont understand my variable names, but i can always clear that up if you need me to.

ill ask that if anyone can directly post some simple code, that would be great. if you can explain it well, then thats good too. dont worry this aint for school, its just been my lifelong dream to make this game, and jumping is kinda essential lol. =)[/code]

Author:  Delos [ Wed Oct 27, 2004 8:50 pm ]
Post subject: 

Okies...

If you want realistic jumping then you need to use vectors.

If you don't know, for any moving object, it's movement and speed can be broken down into horizontal and vertical vectors.

Eg.
An object moving left to right:
vX = +1
vY = 0

An object falling:
vX = -1
vY = 0

An object moving up an elevator (R - L):
vX = -1
vY = +1

Of course I've just used unit values here...you can change then as you need.

Now, for acceleration.
Same idea. An object has vertical and horizontal acceleration. An object that jumps, in this case, has only negative vertical acceleration, this is as the only force acting on it is gravity.
So, if you jump, you only have the force that you exerted at the time you jump - you never gain any more upward acceleration during your flight.
Simple physics...hehehe.

So, for you jumping object, you will consider the horiztonal acceleration however you want (usually zero so the object moves at the same speed while jumping as they did while walking...no speeding up or slowing down).
Vertical acceleration is a constant at a negative value (on Earth it would be about -9.8 m-s^-2).

Object jumps from left to right.
vX = +1
vY = variable...starts at +1, but ends at -1
aX = 0
aY = -1...so every time unit, 1 is subtracted from the velocity (vY), in effect slowing it down. Once it gets negative, the object moves in the opposite direction from which it was initially!)

Want to know more? Look around for particle systems, and study the code.

Author:  Flea [ Thu Oct 28, 2004 7:27 am ]
Post subject: 

Cool stuff Delos!! That was useful. =D

Okay i got my jumping system down, except one little bug. It's with keydown. (little off topic, but its my thread anyway Very Happy)
code:
   
if chars (KEY_UP_ARROW) then
jumpcheck := true


I want jumpcheck to stay true permanently, cause the rest of my jump code only works when jumpcheck is true. What's happening is its relying on me holding down the button instead of just pressing it, as you might have guessed. I tried calling a procedure that has jumpcheck :=true in it but that solves nothing.

I thought of doing
code:

if (up key isnt down) and jumpcheck =true then
jumpcheck := false

but that would just create otehr problems.

I suppose I could use getch but i want my program to stay consistent.

Author:  Cervantes [ Thu Oct 28, 2004 9:00 am ]
Post subject: 

wherever you have your if statement to determine whether your character is touching the ground, inside that structure, make jumpcheck false.

code:

if mage1y <= groundlevel then
   mage1.vY := 0
   mage1.aY := 0
   jumpcheck := false
end if

Author:  Flea [ Thu Oct 28, 2004 6:23 pm ]
Post subject: 

Thx Cervantes. I got that now. Turns out i had a lot more bugs than i had thought. I got rid of most of them however.

Anyway, as you can see I've attached this part of my program with a couple files i sort of made. I know its a pain to open and unzip and all that crap, but =\ dont get upset cause i attached; ya don hafta help if ya don wanna. I dont normally ask for things i can figure out on my own though. take a look at it if you got some spare time. ty!

Author:  Cervantes [ Fri Oct 29, 2004 5:55 pm ]
Post subject: 

Well, here's how I would do the jumping:
code:

var purity, intelligence, wisdom, knowledge, spirit, luck : int := 3
var mage1, mage2 : int
var mage1x, mage1y, win : real := 100
var mage_vx, mage_vy := 0.0 %   \__ Added these two lines. mage_vx and mage_vy are the velocities of the mage in the x and y plane, respectively.  mage_vx never is used, but that's because the mage is magical, and can change direction instantly in midair :P
const gravity := 0.1 %          /   Note that both are real variables.
var jumpcount : int := 0
var boo, jumpcheck : boolean := false
var chars : array char of boolean

mage1 := Pic.FileNew ("mage.jpg")
mage2 := Pic.FileNew ("mage2.jpg")
win := Window.Open ("position:center;center,graphics:800;600")
drawfillbox (0, 0, maxx, maxy, 7)
drawfillbox (0, 0, maxx, 10, 23)
setscreen ("offscreenonly")

loop
    View.Update
    Input.KeyDown (chars)
    if chars (KEY_RIGHT_ARROW) then
        mage1x := mage1x + 1
        boo := false
    elsif chars (KEY_LEFT_ARROW) then
        mage1x := mage1x - 1
        boo := true
    end if

    %Here's how I would take care of the jump sequence
    if chars (KEY_UP_ARROW) and not jumpcheck then %if the up arrow is pressed and the player is not currently jumping then
        jumpcheck := true %make the player enter the jump sequence
        mage_vy := 5 % and set his y velocity to some number, in this case, 5.
    end if
    if jumpcheck then % if the player is currently jumping then
        mage1y += mage_vy % apply the player's y velocity to the player's y position  (same as mage1y := mage1y + mage_vy)
        mage_vy -= gravity % apply gravity to the player's y velocity                (same as mage_vy := mage_vy - gravity)
        if whatdotcolour (round (mage1x), round (mage1y) - 5) = 23 then %if the player is on the ground then
            jumpcheck := false %end the jump sequence
        end if
    end if
    %end jump sequence

    /*%%% start jump %%%
     if chars (KEY_UP_ARROW) then
     jumpcheck := true
     end if
     if jumpcount >= 50 and jumpcheck = true and whatdotcolour (mage1x, mage1y - 5) = 23 then
     jumpcheck := false
     jumpcount := 0
     elsif jumpcheck = true then
     jumpcount := jumpcount + 1
     mage1y := mage1y + 1               % ? %
     View.Update
     end if
     %%% end jump %%%*/
    drawfillbox (round (mage1x) - 1, round (mage1y) - 1, round (mage1x) + 68, round (mage1y) + 87, 7)
    if boo = true then
        Pic.Draw (mage1, round (mage1x), round (mage1y), picCopy)
    else
        Pic.Draw (mage2, round (mage1x), round (mage1y), picCopy)
    end if
    locate (1, 1)
    put "jumpcheck = ", jumpcheck
    if jumpcheck = true then
        put "jumpcount = " + intstr (jumpcount)
    end if
    %The following three lines are covered by the lines: "mage1y += mage_vy" and "mage_vy -= gravity"
    %if whatdotcolour (mage1x, mage1y - 2) = 7 then
    %    mage1y := mage1y - 1
    %end if
    delay (5)
end loop



This may be overhelping, but here's some extra fun things that I did.
Hope it helps Razz
Ideally, though, you'd have to fix the FEATURE of the mage digging through the ground. ie. you'd have to cls the screen each time through the loop and redraw both the mage and his environment, as well as the background, if any.

Author:  Flea [ Sun Oct 31, 2004 12:24 pm ]
Post subject: 

Wow!! I spent liek a day and a half just analyzing this. (never used functions before) Cervantes, that was brilliant. I especially like the obj_width (never would have thought of that, will use it to do x axis collision) and the gravity system. Did a lot more than you had to... and yes it was overwhelming at first of course, but i -totally- get it now, great %'s!

I learned more about turing overall in this than i ever did anywhere else... Very Happy

And I guess you never need = true in an if, eh lol.. nice little tricks to make it simple =) If it turns out theres something in this code im confused about ill pm you, but like i said, the comments were really helpful.

Author:  Flea [ Sat Nov 06, 2004 1:33 pm ]
Post subject: 

Yep, ive been looking at this everyday for a week. Turns out I dont totally understand the function

code:
function ground_collision (x1 : int, y1 : int, obj_width : int, check_under_obj : int, ground_colour : int) : boolean
    for xcheck : x1 .. x1 + obj_width
        for ycheck : y1 + check_under_obj .. y1
            if whatdotcolour (xcheck, ycheck) = ground_colour then
                result true
            end if
        end for
    end for
    result false
end ground_collision


because I tried to do a similar thing for left/right collision, as follows.

code:
for ycheck : floor (mage1y) .. 50 + ceil (mage1y)
        if whatdotcolour (floor (mage1x) + mage_width + 2, ceil (ycheck)) ~= 7 then
            rightmove := true
        else
            rightmove := false
        end if
end for


According to my brain, that should work. However it's not. The problem is, the first line (for ycheck) is not running the code inside the loop for every ycheck.

Just in case that doesent make any sense, ill try to explain it again.



code:
   

loop
for ycheck : floor (mage1y) .. 50 + ceil (mage1y) %it seems this line checks from 50+ceil(mage1y) to 50+ceil(mage1y)
        if whatdotcolour (floor (mage1x) + mage_width + 2, ceil (ycheck)) ~= 7 then
            rightmove := true
        else
            rightmove := false
        end if
end for
keydown stuff here (no errors.)
end loop


so you can see the problem is clearly there in the for. it's not a problem with my keydown code or anything.

ive tried writing a similar for loop for a more simple program, and it worked fine.

theres nothing else i can think of, as i am totally lost right now. and i'd rather not do it with a function, i am gonna try to stay away from them until im more experienced.

Author:  Cervantes [ Mon Nov 08, 2004 8:53 pm ]
Post subject: 

To explain the function:
If I were to do something like the following:
code:

for x : 1 .. 10
  for y : 1 .. 10
    code
  end for
end for

It would execute "code" 100 times. If "code" was some code to check whether (x,y) on a grid is true or false or whatever, it would check a square of 10x10.

In my function, the x forloop is checking from x1 to x1 plus the width of the object. x1 will be mage1x, which is used to draw a picture. Pictures are drawn with their bottom left corner at (x,y). Thus, x1 is the left side of the picutre. x1+ obj_width is the right side of the picture. Thus, the X loop is checking the entire width of the picture.
This doesn't help, though, unless there is a y component to the checking. The Y loop checks from y1 + check_under_obj, to y1. check_under_obj is just some number. Say it's 5. So, it checks from y1 - 5 to y1. Again, Pic.Draw draws the pic with its lower left corner at (x,y). So, y1 is the bottom of the pic, y1 + check_under_obj is a little more complicated. check_under_obj is designed to use the y velocity of the object. Since we're checking for a GROUND collision, vy will be negative, since the mage is moving down, towards the ground. So, y1 + check_under_obj (a negative number) equals a number less than y1. Thus, the ycheck loop checks from a bit under y1 (in relation to the velocity of the player) to y1.
Together, these two for loops check in a grid underneath the mage.
If your mage is travelling with a y velocity of -5 (5 pixels per loop down), the fnc will check in a grid from one side of the pic to the other, and from the bottom of the pic to 5 under the pic. Thus, there is no way that a ground pixel could be skipped over. If the check_under_obj value were hardcoded, to, say, 3, and the mage was travelling down with a velocity of 10, it could be skipped over.
ex: mage is at 100, travelling -10. there is a ground pixel at 93. ground_collision checks a grid from one side of the pic to the other side, and from 100 to 97 (100 - our hardcoded value, 3). The ground pixel is not noticed. Next time through the loop, the mage is at 90 (100 - his velocity, 10). He has passed through the ground pixel! That shouldn't be, and that whole use of velocity in checking the grid is designed to prevent that.
The simpler part of the function is the whatdotcolour, which simply uses that grid that the for loops made for us. It goes through the for loops (the grid) and checks for a ground pixel. If there is one, then it returns true, there was a ground collision. If the for loops complete and it the whatdotcolour line returned false each time, then the ground_collision function returns false, there was no ground collision.

If that makes sense, I made an even more complicated function (well, set of functions) for you. Very Happy

code:

module Collision
    export below, above, left, right
    function below (x : real, y : real, obj_width : int, check_under_obj : real, coll_colour : int) : boolean
        for xcheck : floor (x) .. ceil (x) + obj_width
            for ycheck : floor (y) + floor (check_under_obj) .. ceil (y)
                if whatdotcolour (xcheck, ycheck) = coll_colour then
                    result true
                end if
            end for
        end for
        result false
    end below
    function above (x : real, y : real, obj_width : int, obj_height : int, check_above_obj : real, coll_colour : int) : boolean
        for xcheck : floor (x) .. ceil (x) + obj_width
            for ycheck : floor (y) + obj_height .. ceil (y) + obj_height + ceil (check_above_obj)
                if whatdotcolour (xcheck, ycheck) = coll_colour then
                    result true
                end if
            end for
        end for
        result false
    end above
    function left (x : real, y : real, obj_height : int, check_left_obj : real, coll_colour : int) : boolean
        for xcheck : floor (x) + floor (check_left_obj) .. ceil (x)
            for ycheck : floor (y) .. ceil (y) + obj_height
                if whatdotcolour (xcheck, ycheck) = coll_colour then
                    result true
                end if
            end for
        end for
        result false
    end left
    function right (x : real, y : real, obj_width : int, obj_height : int, check_right_obj : real, coll_colour : int) : boolean
        for xcheck : floor (x) + obj_width .. ceil (x) + obj_width + ceil (check_right_obj)
            for ycheck : floor (y) .. ceil (y) + obj_height
                if whatdotcolour (xcheck, ycheck) = coll_colour then
                    result true
                end if
            end for
        end for
        result false
    end right
end Collision



Other than the module, I changed the functions around a bit. I made it so they input real values, instead of integers, and modified the real values inside the function. Thus, using the function in the actual program is easier.
I also made use of the floor and ceil functions. Logically, I think there may have been a possibility, though negligable, that a collision would be missed. I believe that, with the use of floor and ceil functions, I have eliminated that possibility.

Author:  Cervantes [ Mon Nov 08, 2004 9:22 pm ]
Post subject: 

As for the reason you experienced problems with your code:
unlike the function, this forloop will never exit prematurely. It will go from floor (mage1y) .. ceil (mage1y) + 50 each and every time, and never exit before it goes through 50 or so times.
With the function, however, as soon as the result line is read, the function is ended and that is the result. So, in the function, as soon as one piece of ground, anywhere in the grid that it checked, is found, it exits the function and returns true. However, your style, even if there is a piece of ground in the middle of the for loop, it reallly only checks at ceil(mage1y) + 50. Everything before that is effectively erased, because each time through the for loop, rightmove is assigned a true or false value. If there is ground at ceil (mage1y) + 25, rightmove will be true for a short period of time, until it finds a pixel above that point that isn't the ground colour.
To fix the problem, you could either use the function style, or do something like this:

code:

    %right collision
    rightmove := false
    for ycheck : floor (mage1y) .. 50 + ceil (mage1y)
        if whatdotcolour (floor (mage1x) + mage_width + 2, ycheck) ~= 7 then
            rightmove := true
        end if
    end for


Once again, I hope that is comprehendable. Logic is so seldom easy to explain. Aah, how I long for teleopathy.

Author:  GlobeTrotter [ Mon Nov 08, 2004 11:34 pm ]
Post subject: 

I made a similar type of game.

Go to the 2nd page, its there.
http://www.compsci.ca/v2/viewtopic.php?t=5452

Author:  Flea [ Tue Nov 09, 2004 7:53 am ]
Post subject: 

thanks cervantes, i would have never figured it out own my own.

globetrotter, that game's pretty cool!! much better than some other attempts and the graphics look wicked & so original, especially for being turing-drawn. If you were ever planning on making the yellow ball bigger or adding more complex levels, then using this collision detection would be a bit more sound.


: