Dividing a variable height sprite into Y sections for collission zones

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Robster
:warning: Old Version Published before Godot 3 was released.

Hi all,

Working on Pong.

I want the ball to deflect at different angles and speeds based on where it hits the bat (different zones).

I have an example of the regions I want the ball to react to here:

example bat with deflection zones

Now my bat won’t have colours like this, it’s just to demonstrate how it may work
Hit the top zone, the ball bounces off at a high angle and high speed.
Hit zone just under (zone 2) and it bounces off at a slightly lower angle and slightly lower speed, etc.

Also the bat will change size depending on if I hit a power up to shrink it or a power up to grow it. So the “zones” need to be variable according to total height of the bat.

I’ve been toying with ideas. I could do something like:

var numOfZones = 7
var zoneThatWasHit
var zoneHeight = bat.height / numOfZones #this assumes even sized zones
On ball collide with bat:
    var collisionPoint = work out local collision point on bat somehow

    if collisionPoint > 0 and < zoneHeight:
        zoneThatWasHit = 1
    elif collisionPoint > zoneHeight and < zoneHeight*2:
        zoneThatWasHit = 2
    elif collisionPoint > zoneHeight*2 and < zoneHeight*3:
        zoneThatWasHit = 3
    elif collisionPoint > zoneHeight*3 and < zoneHeight*4:
        zoneThatWasHit = 4
    elif collisionPoint > zoneHeight*4 and < zoneHeight*5:
        zoneThatWasHit = 5
    elif collisionPoint > zoneHeight*5 and < zoneHeight*6:
        zoneThatWasHit = 6
    elif collisionPoint > zoneHeight*6:
        zoneThatWasHit = 7
#now do ball math dependant on zoneThatWasHit

Now, as you can tell, I’m not at status of “programmer” as yet. I have a strong feeling the above would be a piece of poor logic, so I thought I’d ask here for feedback and see what people think.

Any advice I’ll take gladly. Thanks a tonne…

:bust_in_silhouette: Reply From: avencherus

You might get a more pleasing and simpler result, if you get a point of impact, convert it to a percentage based on the current height, and use that in an interpolation formula to decide the weights.

Another idea is to also get a point, then map it to a range between -1 and 1. From there you can use these values to multiply into some weight. The sign should also help split the angle directions from hitting the center.

Thank you for this.

I’ll go with your first idea for now.

I have a ball (RidigBody2D) and it now has collision testing so it knows what object it’s hitting.

I have a test bit of code attached to the ball like so:

func checkCollisions(delta):
	var collisionList = get_colliding_bodies()
	if(get_colliding_bodies().size()>0 ):

		#check if bat
		if collisionList[0].get_name() == "batL":
			findCollisionAreaOnBat("batL")
		elif collisionList[0].get_name() == "batR":
			findCollisionAreaOnBat("batL")

So I also have a function called findCollisionAreaOnBat(bat)but I’m not sure on the approach to take.

How can I go about finding that collision area on the bat?

For example, is there a function that lets you know what local space you (the ball) collided with (object/bat)?

If not, is there some programming mojo I must learn (am I’m happy to learn it!)?

Robster | 2017-02-22 00:47

There are many ways to do this.

If you want more information about the physics events on a RigidBody, you’ll want to implement the _integrate_forces(state): function. The state object contains many things you can query from.

The setup in your case you can use it fine without actually creating your own integration, you can just have it do queries. But if you want to do custom integration as well, you have to make sure you check the custom integration option on the properties, or set it in script.

Outside of that you want to make sure your RigidBody has at least 1 reported contact, their default is 0.

Then a simple example might look like:

func _ready():
	set_max_contacts_reported(1)

func _integrate_forces(state):
	if(state.get_contact_count() > 0):
		print(state.get_contact_local_pos(0)) 

If you want to see what else is on hand for the state, I’d refer you to the docs here: http://docs.godotengine.org/en/stable/classes/class_physics2ddirectbodystate.html#class-physics2ddirectbodystate

Another alternative is to use a raycast that has it’s direction continually aligned to the ball’s heading. You should be able to extract collision details from it.

If you’re determined to use _fixed_process(delta), there are no methods that give you easy queries to the physics state. You can make calls to the Physics2DServer and do special queries, like ray casts and shape intersections.

Outside of that, inside the processing loop you can detect the collision, but you’re on your own as to calculating the collision point. A simple method might getting the collision object from the array returned by get_colliding_bodies(), then doing a line intersect test on the heading of the ball (to some length beyond it’s radius) against the line draw from the top and bottom points of that bat’s contacting edge. Since it’s a straight rectangle and circle, the point of intersection is probably roughly accurate.

avencherus | 2017-02-22 02:25

Firstly thank you for such a detailed answer, it’s very much appreciated.
I’m really not good at math, or programming so I’ve been toying with ideas you’ve presented as well as research from around the net. This is what I’ve come up with.

I can detect the position of the ball hitting the bat now (where on the bat it hits) and I have now normalised it to a -1 +1 range which is great. Now onto the next part.

Thanks again

func findCollisionAreaOnBat(bat):

	#Where is the bat pos?
	var batLocString = str("/root/gameLevel/",bat)
	var batLoc = get_node(batLocString).get_transform().get_origin()

	#Where is the ball pos?
	var ballLoc = self.get_transform().get_origin()

	#what is the bat Height?
	var batHeightString = "/root/gameLevel/%s/Spritebat"
	var actualBatHeightString = batHeightString % bat
	var batHeight = get_node(actualBatHeightString).get_texture().get_height()

	#work out where the ball has hit the bat by subtracting the location of each (bat and ball) origins
	#hitLocation will have a negative or positive number, based on how many pixels from bat centre we are
	var hitLocation = ballLoc.y - batLoc.y

#	#The line below turns the +- pixel count into a normalised value.  So it's between -1 and +1 at each extreme of the bat, depending on where it hits
	#This gives me a specific number I can work with each time, regardless of bat size.  Nice!
	var normalisedhitLocationY = (hitLocation/(batHeight/2));

	

Robster | 2017-02-24 03:11

Sure thing. It looks like you’re off to a good start.

If you want some visual understanding of the cosine and sine, it will generate a unit length vector (a vector that has a length of 1) that swings around a circle.

A good demo for seeing the line’s X/Y lengths can be found here: Interactive Unit Circle

cosine is for X and sine is for Y

Unit length vectors are useful because you can multiply in some distance to change it’s length while keeping it’s direction intact. In your case that unit vector can be imagined as a heading and the scalar as the length it will travel in that game frame.

var velocity = Vector2(cos(angle), -sin(angle)) * speed

avencherus | 2017-02-24 03:27

Thank you. I have to admit I feel … a little challenged here. I started reading the godot vector tutorial and just about died. I understand the idea of direction and magnitude and also I feel I understand a unit vector but it will take me time to really learn it. I’ll do some basic courses online over the next few months.

I’ll ask a new qeustions RE the rest as it’s a different topic and I will aim to keep it focused. Thanks again so very much.

Robster | 2017-02-24 03:36