NaaLaa
problems with collisions in pong - Printable Version

+- NaaLaa (https://www.naalaa.com/forum)
+-- Forum: NaaLaa (https://www.naalaa.com/forum/forum-1.html)
+--- Forum: NaaLaa 7 Questions (https://www.naalaa.com/forum/forum-3.html)
+--- Thread: problems with collisions in pong (/thread-38.html)



problems with collisions in pong - aliensoldier - 01-24-2024

I'm trying to create the pong game and I'm having problems when the ball collides with the top and bottom of the moving player, the ball doesn't bounce, it just goes into the player's graph.

This happens when the player is moving, standing still it does not happen, the problem is in the ball.collision_player method of the ball.

I show all the code I have for now:
Code:
'pong
include "player.n7"
include "player-cpu.n7"
include "ball.n7"

set window "example pong",640,480,false
set redraw off


'objeto-----------------------------------
player = Player()
player_cpu = Player_Cpu()
ball = Ball()

while not keydown(KEY_ESCAPE,true)
    set color 0,0,0
    cls
   
    'objetos-----------------------------
    player.update()
    player.Draw()
    player_cpu.update()
    player_cpu.Draw()
    ball.update()
    ball.Draw()
    'lineas para el fondo------------------ 
    'draw rect 320,32,2,60,true
    'draw rect 320,120,2,60,true
    'draw rect 320,210,2,60,true
    'draw rect 320,300,2,60,true
    'draw rect 320,390,2,60,true
    'draw line 320,32,320,460
    'draw line 330,32,330,460
     
           
    redraw
    fwait 60
wend

Code:
'player
visible player = []

function Player()
    player.Width = 20
    player.Height = 100
    player.x = 32 - player.Width/2
    player.y = 240 - player.Height/2
    player.speed = 5
    player.live = true
   
    player.update = function()
        if this.live = true
            this.move()
        endif
    endfunc
   
    player.Draw = function()
        if this.live = true
            set color 255,0,205
            draw rect this.x,this.y,this.Width,this.Height,true
        endif
    endfunc
   
    player.move = function()
        if keydown(KEY_UP,false) and this.y > 16
            this.y = this.y - this.speed
        elseif keydown(KEY_DOWN,false) and this.y < 370
            this.y = this.y + this.speed
        endif
    endfunc
   
    return player
endfunc

function get_player()
    return player
endfunc

Code:
'player-cpu
include "ball.n7"

visible ball = get_ball()
visible player_cpu = []
function Player_Cpu()
    player_cpu.Width = 20
    player_cpu.Height = 100
    player_cpu.x = 608 - player_cpu.Width/2
    player_cpu.y = 240 - player_cpu.Height/2
    player_cpu.speed = 5
    player_cpu.live = true
 
    player_cpu.update = function()
    endfunc
 
    player_cpu.Draw = function()
        if this.live
            set color 255,204,0
            draw rect this.x,this.y,this.Width,this.Height,true
        endif
    endfunc
 
    return player_cpu
endfunc
function get_player_cpu()
    return player_cpu
endfunc


Code:
'ball
include "player.n7"
include "miscellaneous.n7"
include "player-cpu.n7"

visible player = get_player()
visible player_cpu = get_player_cpu()
visible ball = []

function Ball()
    ball.Width = 16
    ball.Height = 16
    ball.x = 320 - ball.Width / 2
    ball.y = 240 - ball.Height / 2
    ball.speedX = 2
    ball.speedY = 2
    ball.live = true
 
    ball.update = function()
        if this.live = true
            this.move()
            this.bounce()
            this.collision_player()
        endif
    endfunc
 
    ball.Draw = function()
        if this.live = true
            set  color 200,200,200
            draw rect this.x,this.y,this.Width,this.Height,true
            'draw ellipse this.x,this.y,9,9,true
        endif
    endfunc
 
    ball.move = function()
        this.x = this.x + this.speedX
        this.y = this.y + this.speedY
    endfunc
 
    ball.bounce = function()
        if this.x <= 0 or this.x >= 640
            this.speedX = this.speedX * -1
        endif
     
        if this.y <= 0 or this.y >= 480
            this.speedY = this.speedY * -1
        endif
    endfunc
 
    ball.collision_player = function()
        'colision con la x
        if collision_rect(this.x+this.speedX,this.y,this.Width,this.Height,player.x,player.y,player.Width,player.Height)
            this.speedX = this.speedX * -1
        endif
        'colision con la y
        if collision_rect(this.x,this.y+this.speedY,this.Width,this.Height,player.x,player.y,player.Width,player.Height)
            this.speedY = this.speedY * -1
        endif
    endfunc

    return ball
endfunc

function get_ball()
    return ball
endfunc

Code:
'Miscellaneous

'variables y funciones para manejar los puntos
visible score_player = 0
visible score_player_cpu = 0

function get_score_player()
    return score_player
endfunc

function get_score_player_cpu()
    return score_player_cpu
endfunc

'funcion de colision--------------------------------
function collision_rect(x1,y1,w1,h1,x2,y2,w2,h2)
    return x1 + w1 > x2 and x1 < x2 + w2 and
            y1 + h1 > y2 and y1 < y2 + h2
endfunc



RE: problems with collisions in pong - Marcus - 01-24-2024

I promise to get back to you when I've looked at your code (and the code you posted in another thread)! I'll try and get the time tomorrow, been so busy lately Smile


RE: problems with collisions in pong - Marcus - 01-25-2024

These things can become quite tricky, and I'm not sure if my solution works well in all cases.

If the ball and the paddle overlap, I decide wether the ball should bounce vertically or horizontally by looking at along which axis they overlap the least. If they overlap the least along the x-axis, it's a horizontal bounce, else a vertical.

ball.n7
Code:
...
    ball.collision_player = function()
        'colision con la x
        'if collision_rect(this.x+this.speedX,this.y,this.Width,this.Height,player.x,player.y,player.Width,player.Height)
        '    this.speedX = this.speedX * -1
        'endif
        'colision con la y
        'if collision_rect(this.x,this.y+this.speedY,this.Width,this.Height,player.x,player.y,player.Width,player.Height)
        '    this.speedY = this.speedY * -1
        'endif
       
        ' Marcus.
        ' Check if they overlap.
        if collision_rect(this.x,this.y,this.Width,this.Height,player.x,player.y,player.Width,player.Height)
            ' Calculate delta x and delta y, between the center of the ball and the center of the
            ' paddle. Divide the delta x value width the width of the paddle and delta y with the
            ' height of the paddle to normalize them (make comparison valid).
            dx = (this.x + this.Width/2 - (player.x + player.Width/2))/player.Width
            dy = (this.y + this.Height/2 - (player.y + player.Height/2))/player.Height
            ' If dx is higher, it means that there's less overlapping along the x-axis. In that
            ' case bounce left or right.
            if |dx| >= |dy|
                ' dx < 0, ball should bounce to the left, and we also move the ball to the left so
                ' that there's no longer any collision.
                if dx < 0
                    this.speedX = -|this.speedX|
                    this.x = player.x - this.Width               
                ' dx > 0, ball should bounce to the right, and move the ball to the right of the
                ' paddle.
                else
                    this.speedX = |this.speedX|
                    this.x = player.x + player.Width
                endif
            ' dy is higher, same principle as for dx :)
            else
                if dy < 0
                    this.speedY = -|this.speedY|
                    this.y = player.y - this.Height
                else
                    this.speedY = |this.speedY|
                    this.y = player.y + player.Height
                endif
            endif
        endif
    endfunc
    ...

Hope this helps! There are probably much better ways of doing it.

Edit: By the way, the code looks nice! I see now that you're already using "getters" and "setters" to share variables between files. In my opinion that makes the code way more readable than it would be if you could make variables visible across files - but that's just my opinion Smile


RE: problems with collisions in pong - aliensoldier - 01-25-2024

Thank you very much, it works very well. Smile

I have found a problem in naalaa that may be a bug or maybe it works like this.

For example, I am calling the ball from player cpu to access the "y" of the ball and make the player cpu move following the ball, but if I call the player cpu from ball to make the collision with the cpu I get fails.

If I access the data of the ball from the player cpu I cannot access the data of the player cpu from the ball. I don't know if it is a naalaa bug or it works this way, I have had to make some changes so that everything works from the ball but the code is growing a lot.


RE: problems with collisions in pong - Marcus - 01-25-2024

(01-25-2024, 08:24 PM)aliensoldier Wrote: Thank you very much, it works very well. Smile

I have found a problem in naalaa that may be a bug or maybe it works like this.

For example, I am calling the ball from player cpu to access the "y" of the ball and make the player cpu move following the ball, but if I call the player cpu from ball to make the collision with the cpu I get fails.

If I access the data of the ball from the player cpu I cannot access the data of the player cpu from the ball. I don't know if it is a naalaa bug or it works this way, I have had to make some changes so that everything works from the ball but the code is growing a lot.

That sounds strange. Could you write a simple example that shows the bug or just add something to your game that makes the bug appear?


RE: problems with collisions in pong - aliensoldier - 01-25-2024

I have updated the code of the ball and the player cpu, in the ball we have get_player_cpu(), and in the cpu we have get_ball(). With this I have recreated the error that I get with a more complete code


RE: problems with collisions in pong - Marcus - 01-25-2024

(01-25-2024, 08:59 PM)aliensoldier Wrote: I have updated the code of the ball and the player cpu, in the ball we have get_player_cpu(), and in the cpu we have get_ball(). With this I have recreated the error that I get with a more complete code

I have to make it short, because I gotta go to bed Smile

player.n7, ball.n7 and player-cpu.n7 is not where you should implement get_player, get_ball and get_player_cpu. Nor should you in these file store any objects that are used in the game. For example, player.n7 should only look like this:

Code:
'player

function Player()
    player = []
    player.Width = 20
    player.Height = 100
    player.x = 32 - player.Width/2
    player.y = 240 - player.Height/2
    player.speed = 5
    player.live = true
 
    player.update = function()
        if this.live = true
            this.move()
        endif
    endfunc
 
    player.Draw = function()
        if this.live = true
            set color 255,0,205
            draw rect this.x,this.y,this.Width,this.Height,true
        endif
    endfunc
 
    player.move = function()
        if keydown(KEY_UP,false) and this.y > 16
            this.y = this.y - this.speed
        elseif keydown(KEY_DOWN,false) and this.y < 370
            this.y = this.y + this.speed
        endif
    endfunc
 
    return player
endfunc

ball.n7 should look like this:

Code:
'ball
include "player.n7"
include "miscellaneous.n7"

' Marcus, use the getter when needed instead. ball.n7 should just be the implementation for a ball.
'visible player = get_player()

function Ball()
    ball = []
    ball.Width = 16
    ball.Height = 16
    ball.x = 320 - ball.Width / 2
    ball.y = 240 - ball.Height / 2
    ball.speedX = 2
    ball.speedY = 2
    ball.live = true
  
    ball.update = function()
        if this.live = true
            this.move()
            this.bounce()
            this.collision_player()
        endif
    endfunc
  
    ball.Draw = function()
        if this.live = true
            set  color 200,200,200
            draw rect this.x,this.y,this.Width,this.Height,true
            'draw ellipse this.x,this.y,9,9,true
        endif
    endfunc
  
    ball.move = function()
        this.x = this.x + this.speedX
        this.y = this.y + this.speedY
    endfunc
  
    ball.bounce = function()
        if this.x <= 0 or this.x >= 640
            this.speedX = this.speedX * -1
        endif
      
        if this.y <= 0 or this.y >= 480
            this.speedY = this.speedY * -1
        endif
    endfunc
  
    ball.collision_player = function()
        'colision con la x
        'if collision_rect(this.x+this.speedX,this.y,this.Width,this.Height,player.x,player.y,player.Width,player.Height)
        '    this.speedX = this.speedX * -1
        'endif
        'colision con la y
        'if collision_rect(this.x,this.y+this.speedY,this.Width,this.Height,player.x,player.y,player.Width,player.Height)
        '    this.speedY = this.speedY * -1
        'endif
      
        ' Marcus.
        player = get_player()
        ' Check if they overlap.
        if collision_rect(this.x,this.y,this.Width,this.Height,player.x,player.y,player.Width,player.Height)
            ' Calculate delta x and delta y, between the center of the ball and the center of the
            ' paddle. Divide the delta x value width the width of the paddle and delta y with the
            ' height of the paddle to normalize them (make comparison valid).
            dx = (this.x + this.Width/2 - (player.x + player.Width/2))/player.Width
            dy = (this.y + this.Height/2 - (player.y + player.Height/2))/player.Height
            ' If dx is higher, it means that there's less overlapping along the x-axis. In that
            ' case bounce left or right.
            if |dx| >= |dy|
                ' dx < 0, ball should bounce to the left, and we also move the ball to the left so
                ' that there's no longer any collision.
                if dx < 0
                    this.speedX = -|this.speedX|
                    this.x = player.x - this.Width              
                ' dx > 0, ball should bounce to the right, and move the ball to the right of the
                ' paddle.
                else
                    this.speedX = |this.speedX|
                    this.x = player.x + player.Width
                endif
            ' dy is higher, same principle as for dx :)
            else
                if dy < 0
                    this.speedY = -|this.speedY|
                    this.y = player.y - this.Height
                else
                    this.speedY = |this.speedY|
                    this.y = player.y + player.Height
                endif
            endif
        endif
    endfunc

    return ball
endfunc

It is in pong.n7 that you call Player(), Ball() etc to create the actual game objects. Therefor it is in pong.n7 that you should implement get_player, get_ball and get_player_cpu, because it is pong.n7 that owns and manages these objects.

Code:
'pong
include "player.n7"
include "player-cpu.n7"
include "ball.n7"

' Marcus, objects and getters. This file is the owner of the sprites, so this is where the getters
' belong.
visible player, player_cpu, ball
function get_player(); return player; endfunc
function get_player_cpu(); return player_cpu; endfunc
function get_ball(); return ball; endfunc

set window "example pong",640,480,false
set redraw off

'objeto-----------------------------------
player = Player()
player_cpu = Player_Cpu()
ball = Ball()

while not keydown(KEY_ESCAPE,true)
    set color 0,0,0
    cls
 
    'objetos-----------------------------
    player.update()
    player.Draw()
    player_cpu.update()
    player_cpu.Draw()
    ball.update()
    ball.Draw()
    'lineas para el fondo------------------
    'draw rect 320,32,2,60,true
    'draw rect 320,120,2,60,true
    'draw rect 320,210,2,60,true
    'draw rect 320,300,2,60,true
    'draw rect 320,390,2,60,true
    'draw line 320,32,320,460
    'draw line 330,32,330,460
   
         
    redraw
    fwait 60
wend

The error you see should disappear if you do it like this. The problem in your code now is that when this line executes in player-cpu.n7:

Code:
visible ball = get_ball()

the ball hasn't even been created yet. So when you use that visible ball variable in your cpu update function things go very wrong. You shouldn't store the ball as a visible variable in player-cpu.n7 at all. Rather call get_ball() (implemented in pong.n7) in the player cpu update function.

Or why not just pass the ball as a parameter to player.update and player_cpu.update?


RE: problems with collisions in pong - aliensoldier - 01-26-2024

Passing the instance in the update method is the only one that has worked well. Smile