Games change of direction in collision

developer_123

Member
Licensed User
I have a character that I created in Tiled, and he initially moves to the right or to the left (whichever, the initial orientation is random, since the application is started the character moves constantly). What I need is that when detecting a collision with another object, it automatically changes the direction of movement, in the opposite direction. I've checked and can't find the way. I appreciate any guidance!
 
Last edited:

ilan

Expert
Licensed User
You just change the velocity of the object to a negative value or you add a force to the body (negative value)

how are you moving your body right now? Post some code.
 

ilan

Expert
Licensed User
i assume that you are using xui2d. so you also should note that the collision is detected in 2 events.

B4X:
Private Sub World_BeginContact (Contact As B2Contact)
...
End Sub

Private Sub World_EndContact(Contact As B2Contact)
...
End Sub

here you can detect if there was a collision and what bodies has collide and then you can change the velocity of the body or force. (i dont know how you move the body)

in this example (ilan's first app) you can see how i apply a force to the ball after they touch the invisible body that should simulate air coming from a ventilator.

B4X:
Private Sub World_BeginContact (Contact As B2Contact)
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact,  "ball")
    If bodies <> Null Then
        Dim data As BallData = bodies.ThisBody.Tag
        
        If bodies.OtherBody.Name = "forceright" Then
            data.IsTouched = True
            data.Force.Set(0.1, 0)
        else if bodies.OtherBody.Name = "forceup" Then
            data.IsTouched = True
            data.Force.Set(0, 0.5)
        End If
    End If
End Sub

after the ball has ended the connection i remove the force

B4X:
Private Sub World_EndContact(Contact As B2Contact)
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact,  "ball")
    If bodies <> Null Then
        Dim data As BallData = bodies.ThisBody.Tag
        If bodies.OtherBody.Name.StartsWith("force") Then
            data.IsTouched = False
            data.Force.Set(0, 0)
        End If
    End If
End Sub

have a look at the X2 examples.
 

ilan

Expert
Licensed User
i really would like to make games with xui2d but the thing that holds me is the rendering part. this is something i still need to investigate.
i know i can put the frames in tiled but sometimes i just want to render something on the screen without creating a body. this is very simple in libgdx.
 

developer_123

Member
Licensed User
[QUOTE = "ilan, publicación: 759606, miembro: 27471"]
You just change the velocity of the object to a negative value or you add a force to the body (negative value)

how are you moving your body right now? Post some code.
[/ CITAR]

Thank you ilan for your reply. My body I'm moving with the code

Public Sub Tick' (GS As X2GameStep)
bw.Body.LinearVelocity.X=0.5
End Sub

in a new class that I created specifically for this body, and that I am calling from the Tick of game. That works well for me and I already did a little test for the change of direction creating a button that changes the sign of the vector x component. My issue is that I didn't know how to detect the collision to do it automatically, but I'm seeing that you uploaded an example of collision detection code that I wasn't familiar with, in addition to the example Erel is relating in his answer. So I'm going to study these 2 codes and do tests in the afternoon and I'll be reporting on my result. Many thanks to you and Erel for the information.
 

developer_123

Member
Licensed User
i really would like to make games with xui2d but the thing that holds me is the rendering part. this is something i still need to investigate.
i know i can put the frames in tiled but sometimes i just want to render something on the screen without creating a body. this is very simple in libgdx.

I haven't got there yet, but I have noticed that when assigning a .png image with good resolution to my character (my character is small in proportion to the cell phone screen), this image becomes too pixelated, and it doesn't look sharp. It would be good to know how to correct this, because although at this moment I am in the physical logic of my application, I will soon reach the rendering time.
 

developer_123

Member
Licensed User
i assume that you are using xui2d. so you also should note that the collision is detected in 2 events.

B4X:
Private Sub World_BeginContact (Contact As B2Contact)
...
End Sub

Private Sub World_EndContact(Contact As B2Contact)
...
End Sub

here you can detect if there was a collision and what bodies has collide and then you can change the velocity of the body or force. (i dont know how you move the body)

in this example (ilan's first app) you can see how i apply a force to the ball after they touch the invisible body that should simulate air coming from a ventilator.

B4X:
Private Sub World_BeginContact (Contact As B2Contact)
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact,  "ball")
    If bodies <> Null Then
        Dim data As BallData = bodies.ThisBody.Tag
        
        If bodies.OtherBody.Name = "forceright" Then
            data.IsTouched = True
            data.Force.Set(0.1, 0)
        else if bodies.OtherBody.Name = "forceup" Then
            data.IsTouched = True
            data.Force.Set(0, 0.5)
        End If
    End If
End Sub

after the ball has ended the connection i remove the force

B4X:
Private Sub World_EndContact(Contact As B2Contact)
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact,  "ball")
    If bodies <> Null Then
        Dim data As BallData = bodies.ThisBody.Tag
        If bodies.OtherBody.Name.StartsWith("force") Then
            data.IsTouched = False
            data.Force.Set(0, 0)
        End If
    End If
End Sub

have a look at the X2 examples.


Thanks ilan for the code, it is very easy to detect collisions this way!

In summary, my application requires several characters that do not collide with each other, but that do collide with other elements, and following the indications of EREL in this thread https://www.b4x.com/android/forum/threads/xui2d-to -collide-or-not-to-collide.97144 / # post-760005, I have managed not to collide! (Thanks Erel for this.)

However, after making several code attempts to make my character (man) hit the wall and change direction (I defined my character as an arrangement in Game and defined a class for my character), I am presenting 2 problems.

1) When I create a single character "mMan (1)" when it hits the wall, it presents a strange behavior. Initially I thought that my logic was wrong (and it was very strenuous since I create like 3 different routines thinking that I could not find the logic), but when I created several "mMan (5)" characters, I realized that the algorithm does work some of they however strange behavior continues to present itself.

2) When creating several characters I see that they do not move independently, instead there are characters (man) that without having hit the wall are returned when others do collide.


this is the code in the man class for change direction of man
B4X:
Public Sub Tick 
	If speed_man>0 Then
		direction_man="right"
	Else
		If speed_man<0 Then
			direction_man="left"
		Else
			If speed_man=0 Then
				direction_man="still"
			End If
		End If
	End If
	If direction_man="right" Then
		bw.Body.LinearVelocity.X=speed_man
		bw.FlipHorizontal=False
	Else
		If direction_man="left" Then
			bw.Body.LinearVelocity.X=speed_man
			bw.FlipHorizontal=True
			If direction_man="still" Then
				bw.Body.LinearVelocity.X=speed_man 'if still so nothing happens
			End If
		End If
	End If
	If collide_with_wall=True  Then
		If direction_man="right" Then
			speed_man=-1
		Else
			If direction_man="left" Then
				speed_man=1
			End If
		End If
	End If
End Sub

this is the code in Game
B4X:
Sub Class_Globals
	Dim quantity_mans As Int =5
	Public mMan(quantity_mans) As Man
.
.
.
End sub

Private Sub World_BeginContact (Contact As B2Contact)
	Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact, "man")
	If bodies = Null Then Return
	If bodies.OtherBody.Name = "wall" Then
		button2.Text="touch wall"'this line just is an indicator
		For identifier_mans=0 To quantity_mans - 1
			mMan(identifier_mans).collide_with_wall=True
		Next
	End If
End Sub

Private Sub World_EndContact(Contact As B2Contact)
	Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact, "man")
	If bodies <> Null Then
		button2.Text="no touch"'this line just is an indicator
		For identifier_mans=0 To quantity_mans - 1	
			mMan(identifier_mans).collide_with_wall=False
		Next
End If
End Sub

And this is the behavior with 1 and severals characters. (some men stick to the wall)

gif2.gif
 
Last edited:

developer_123

Member
Licensed User
Thanks ilan for the code, it is very easy to detect collisions this way!

In summary, my application requires several characters that do not collide with each other, but that do collide with other elements, and following the indications of EREL in this thread https://www.b4x.com/android/forum/threads/xui2d-to -collide-or-not-to-collide.97144 / # post-760005, I have managed not to collide! (Thanks Erel for this.)

However, after making several code attempts to make my character (man) hit the wall and change direction (I defined my character as an arrangement in Game and defined a class for my character), I am presenting 2 problems.

1) When I create a single character "mMan (1)" when it hits the wall, it presents a strange behavior. Initially I thought that my logic was wrong (and it was very strenuous since I create like 3 different routines thinking that I could not find the logic), but when I created several "mMan (5)" characters, I realized that the algorithm does work some of they however strange behavior continues to present itself.

2) When creating several characters I see that they do not move independently, instead there are characters (man) that without having hit the wall are returned when others do collide.


this is the code in the man class for change direction of man
B4X:
Public Sub Tick 
	If speed_man>0 Then
		direction_man="right"
	Else
		If speed_man<0 Then
			direction_man="left"
		Else
			If speed_man=0 Then
				direction_man="still"
			End If
		End If
	End If
	If direction_man="right" Then
		bw.Body.LinearVelocity.X=speed_man
		bw.FlipHorizontal=False
	Else
		If direction_man="left" Then
			bw.Body.LinearVelocity.X=speed_man
			bw.FlipHorizontal=True
			If direction_man="still" Then
				bw.Body.LinearVelocity.X=speed_man 'if still so nothing happens
			End If
		End If
	End If
	If collide_with_wall=True  Then
		If direction_man="right" Then
			speed_man=-1
		Else
			If direction_man="left" Then
				speed_man=1
			End If
		End If
	End If
End Sub

this is the code in Game
B4X:
Sub Class_Globals
	Dim quantity_mans As Int =5
	Public mMan(quantity_mans) As Man
.
.
.
End sub

Private Sub World_BeginContact (Contact As B2Contact)
	Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact, "characterman")
	If bodies = Null Then Return
	If bodies.OtherBody.Name = "pared" Then
		button2.Text="touch wall"'this line just is an indicator
		For identifier_mans=0 To quantity_mans - 1
			mMan(identifier_mans).collide_with_wall=True
		Next
	End If
End Sub

Private Sub World_EndContact(Contact As B2Contact)
	Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact, "characterman")
	If bodies <> Null Then
		button2.Text="no touch"'this line just is an indicator
		For identifier_mans=0 To quantity_mans - 1	
			mMan(identifier_mans).collide_with_wall=False
		Next
End If
End Sub

And this is the behavior with 1 and severals characters. (some men stick to the wall)

View attachment 99164
gif4.gif

This is the behavior with severals characters
 

ilan

Expert
Licensed User
in your tick event you have this code:

B4X:
If collide_with_wall=True  Then
        If direction_man="right" Then
            speed_man=-1
        Else
            If direction_man="left" Then
                speed_man=1
            End If
        End If
    End If

the problem is that you change the speed of the character but don't change the "collide_with_wall" boolean.
so what happens is that the caracter is still touching the wall and always change the speed from positive to negative and again.

when the condition happens (collide_with_wall) you can change it to false to avoid such behavior.

try to change it the code to this and see if it works.

B4X:
If collide_with_wall=True  Then
       collide_with_wall = false '<------
        If direction_man="right" Then
            speed_man=-1
        Else
            If direction_man="left" Then
                speed_man=1
            End If
        End If
    End If

the second issue with more then 1 character looks to me like the speed_man you set is a global variable and all characters are using the same speed.
what means if 1 character hits the wall and change the speed all characters will move to the same direction.
you should create a type object and put all characters you create in a list and with a loop go through all characters and do the stuff you are doing.

i have posted lot of example where you have a list with objects and every object is updated with its own properties.
have a look at this example: (its the last i have added)

you can see that i create each coin with its properties and add it to a list. then in a loop i go through all coins. you can use this for any other scenario like your characters.
once you understand the logic it will be very simple.
 

ilan

Expert
Licensed User
btw, what would you say to change the tick event code from this:

B4X:
Public Sub Tick
    If speed_man>0 Then
        direction_man="right"
    Else
        If speed_man<0 Then
            direction_man="left"
        Else
            If speed_man=0 Then
                direction_man="still"
            End If
        End If
    End If
    If direction_man="right" Then
        bw.Body.LinearVelocity.X=speed_man
        bw.FlipHorizontal=False
    Else
        If direction_man="left" Then
            bw.Body.LinearVelocity.X=speed_man
            bw.FlipHorizontal=True
            If direction_man="still" Then
                bw.Body.LinearVelocity.X=speed_man 'if still so nothing happens
            End If
        End If
    End If
    If collide_with_wall=True  Then
        If direction_man="right" Then
            speed_man=-1
        Else
            If direction_man="left" Then
                speed_man=1
            End If
        End If
    End If
End Sub

to this:

B4X:
Public Sub Tick
    If collide_with_wall Then
        collide_with_wall = False
        speed_man = speed_man*-1
    End If
    bw.Body.LinearVelocity.X=speed_man
    bw.FlipHorizontal=speed_man>0
End Sub
 

developer_123

Member
Licensed User
btw, what would you say to change the tick event code from this:

B4X:
Public Sub Tick
    If speed_man>0 Then
        direction_man="right"
    Else
        If speed_man<0 Then
            direction_man="left"
        Else
            If speed_man=0 Then
                direction_man="still"
            End If
        End If
    End If
    If direction_man="right" Then
        bw.Body.LinearVelocity.X=speed_man
        bw.FlipHorizontal=False
    Else
        If direction_man="left" Then
            bw.Body.LinearVelocity.X=speed_man
            bw.FlipHorizontal=True
            If direction_man="still" Then
                bw.Body.LinearVelocity.X=speed_man 'if still so nothing happens
            End If
        End If
    End If
    If collide_with_wall=True  Then
        If direction_man="right" Then
            speed_man=-1
        Else
            If direction_man="left" Then
                speed_man=1
            End If
        End If
    End If
End Sub

to this:

B4X:
Public Sub Tick
    If collide_with_wall Then
        collide_with_wall = False
        speed_man = speed_man*-1
    End If
    bw.Body.LinearVelocity.X=speed_man
    bw.FlipHorizontal=speed_man>0
End Sub
in your tick event you have this code:

B4X:
If collide_with_wall=True  Then
        If direction_man="right" Then
            speed_man=-1
        Else
            If direction_man="left" Then
                speed_man=1
            End If
        End If
    End If

the problem is that you change the speed of the character but don't change the "collide_with_wall" boolean.
so what happens is that the caracter is still touching the wall and always change the speed from positive to negative and again.

when the condition happens (collide_with_wall) you can change it to false to avoid such behavior.

try to change it the code to this and see if it works.

B4X:
If collide_with_wall=True  Then
       collide_with_wall = false '<------
        If direction_man="right" Then
            speed_man=-1
        Else
            If direction_man="left" Then
                speed_man=1
            End If
        End If
    End If

the second issue with more then 1 character looks to me like the speed_man you set is a global variable and all characters are using the same speed.
what means if 1 character hits the wall and change the speed all characters will move to the same direction.
you should create a type object and put all characters you create in a list and with a loop go through all characters and do the stuff you are doing.

i have posted lot of example where you have a list with objects and every object is updated with its own properties.
have a look at this example: (its the last i have added)

you can see that i create each coin with its properties and add it to a list. then in a loop i go through all coins. you can use this for any other scenario like your characters.
once you understand the logic it will be very simple.
Thank you very much ilan, I can't believe that I didn't think about setting the boolean false (this works well only for a 1 character). It definitely goes a long way when someone else sees things from another angle. I spent a lot of time burning my head. Thank you! Regarding the use of the list for various characters, I will begin to review what you just suggested and I will be commenting on my results.
 
Last edited:

developer_123

Member
Licensed User
btw, what would you say to change the tick event code from this:

B4X:
Public Sub Tick
    If speed_man>0 Then
        direction_man="right"
    Else
        If speed_man<0 Then
            direction_man="left"
        Else
            If speed_man=0 Then
                direction_man="still"
            End If
        End If
    End If
    If direction_man="right" Then
        bw.Body.LinearVelocity.X=speed_man
        bw.FlipHorizontal=False
    Else
        If direction_man="left" Then
            bw.Body.LinearVelocity.X=speed_man
            bw.FlipHorizontal=True
            If direction_man="still" Then
                bw.Body.LinearVelocity.X=speed_man 'if still so nothing happens
            End If
        End If
    End If
    If collide_with_wall=True  Then
        If direction_man="right" Then
            speed_man=-1
        Else
            If direction_man="left" Then
                speed_man=1
            End If
        End If
    End If
End Sub

to this:

B4X:
Public Sub Tick
    If collide_with_wall Then
        collide_with_wall = False
        speed_man = speed_man*-1
    End If
    bw.Body.LinearVelocity.X=speed_man
    bw.FlipHorizontal=speed_man>0
End Sub
The code you suggest is much shorter and works the same !!, only that the flip must change sign when the speed is -1, but that is a minor issue. In fact, one of the 3 routines I did last night was similar to the one you suggested, and I came to think that the error was in the use of the 2 continuous mathematical operators (speed_man * -1). Now it is clear that B4X allows this written form without inconvenience.
 
Top