2018-06-12 21:44:30

Hi all.
So, I'm making a game with python 3, though, I don't get how can I collide 2 objects. Objects have attributes x, y, z.
The current system that I use, detect the range by checking if the x coord is the same and if the difference between attacker and target are minor or equal to the range of the attack.
Though it doesn't work.
Do you suggest libraries, or different formulas? thanks

If you want to get in touch with me you can follow me on Twitter
have a nice day.
Paul

2018-06-13 03:36:42 (edited by magurp244 2018-06-13 05:22:52)

Generally collision detection works by defining an area with a width, height, or depth, then checking if those two area's overlap, such as with Bounding Box Collision. As an example:

#A and B are the two square objects to be tested for collision
#the root x and y coords of each object are for their lower left corners
def collide(a, b):
#if the top of A is less than B
    if a.y + a.height < b.y:
        return False
#if the bottom of A is greater than the top of B
    if a.y > b.y + b.height:
        return False
#if the far right of A is less than the left of B
    if a.x + a.width < b.x:
        return False
#if the left of A is greater than the far right of B
    if a.x > b.x + b.width:
        return False
#if any of the above are true, then a collision has not occured
    return True

With the above snippet, you could check if an object with a width and height intersects with another. In the case of handling weapon collisions, you could treat bullets as a separate object for the purposes of collision detection and test the bullet with the target if they get within a certain threshold. For melee attacks you could apply an offset value or treat the melee strike as a separate object. For doing 3D Bounding Boxes all you need to do is add a few extra lines like so:

def collide(a, b):
    if a.y + a.height < b.y:
        return False
    if a.y > b.y + b.height:
        return False
    if a.x + a.width < b.x:
        return False
    if a.x > b.x + b.width:
        return False
    if a.z + a.depth < b.z:
        return False
    if a.z > b.z + b.depth:
        return False
    return True

There's alot more to it depending on what you have in mind, lie spacial partitioning, octree's, etc. But for simple collision this should suffice.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

Thumbs up

2018-06-13 08:48:48

If you're using pygame, check out the rect class.

Some of my games
Keep up to date by following @Jeqofire on twitter!
Ear Ninja?

Thumbs up

2018-06-13 11:39:09

@post 2, I've used a separate bullet class, but it seem to not working. The function that checks, seem to not starting even if the class is calling it
@post 3, no, I'm not using pygame. I'm using pyglet for key handles, and I'm using sound_lib.

If you want to get in touch with me you can follow me on Twitter
have a nice day.
Paul

2018-06-14 05:12:17

Without sharing your code its difficult to say what may be going wrong, but I can provide a working example, in the following code there are two boxes, the one on the left is the player and the one on the right is an enemy. Pressing spacebar will fire a bullet to the right from the player, colliding with and removing both the enemy and the bullet.

import pyglet
from pyglet.window import key

class Example(pyglet.window.Window):
    def __init__(self):
        super(Example, self).__init__(640, 480, resizable=False, fullscreen=False, caption="Example")
        self.clear()

    #key input buffer
        self.key_input = []
    #add objects to be updated
        self.objects = []
    #add player
        self.objects.append(player(5,40,self.objects))
    #add enemy
        self.objects.append(enemy(80,40,self.objects))
    #call main window update function
        pyglet.clock.schedule_interval(self.update, .01)

    def update(self,dt):
    #update objects
        for a in self.objects:
            a.update(self.key_input)

    #check collision of objects
        for a in xrange(0,len(self.objects),1):
            for b in xrange(0,len(self.objects),1):
                if a != b and len(self.objects) > 1:
                    result = self.collide(self.objects[a],self.objects[b])
                #if collision occurred, remove both objects from update list
                    if result == True:
                        self.objects.remove(self.objects[a])
                        self.objects.remove(self.objects[b-1])

    #clear key input buffer
        self.key_input = []
    #draw screen
        self.draw()

    def draw(self):
        self.clear()
        for a in self.objects:
            a.draw()

    def on_key_press(self,symbol,modifiers):
        if symbol == key.ESCAPE:
            self.close()
        self.key_input.append(key.symbol_string(symbol) + " press")

    def collide(self, a, b):
        if a.y + a.height< b.y:
            return False
        if a.y > b.y + b.height:
            return False
        if a.x + a.width < b.x:
            return False
        if a.x > b.x + b.width:
            return False
        return True 


class player(object):
    def __init__(self,x,y,objects):
        self.x = x
        self.y = y
        self.width = 32
        self.height = 32
        self.objects = objects

    def update(self,key):
    #fire a bullet, which adds it to the main objects list to be updated
        if 'SPACE press' in key:
            self.objects.append(bullet(self.x+self.width+2,self.y+(self.height/2),self.objects))

    def draw(self):
        pyglet.graphics.draw(8, pyglet.gl.GL_LINES, ("v2f", (self.x,self.y,self.x+self.width,self.y,
                                                             self.x+self.width,self.y+self.height,self.x+self.width,self.y,
                                                             self.x+self.width,self.y+self.height,self.x,self.y+self.height,
                                                             self.x,self.y+self.height,self.x,self.y)))


class enemy(object):
    def __init__(self,x,y,objects):
        self.x = x
        self.y = y
        self.width = 32
        self.height = 32
        self.objects = objects

    def update(self,key):
        pass

    def draw(self):
        pyglet.graphics.draw(8, pyglet.gl.GL_LINES, ("v2f", (self.x,self.y,self.x+self.width,self.y,
                                                             self.x+self.width,self.y+self.height,self.x+self.width,self.y,
                                                             self.x+self.width,self.y+self.height,self.x,self.y+self.height,
                                                             self.x,self.y+self.height,self.x,self.y)))


class bullet(object):
    def __init__(self,x,y,objects):
        self.x = x
        self.y = y
        self.width = 6
        self.height = 6
        self.objects = objects

    def update(self,key):
        self.x += 1
    #if bullet eceeds the right side of the window, remove it from update list
        if self.x > 640:
            self.objects.remove(self)

    def draw(self):
        pyglet.graphics.draw(8, pyglet.gl.GL_LINES, ("v2f", (self.x,self.y,self.x+self.width,self.y,
                                                             self.x+self.width,self.y+self.height,self.x+self.width,self.y,
                                                             self.x+self.width,self.y+self.height,self.x,self.y+self.height,
                                                             self.x,self.y+self.height,self.x,self.y)))

if __name__ == '__main__':
    window = Example()
    pyglet.app.run()
-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

Thumbs up

2018-06-14 10:14:32

here's my code

from timer import timer # AGK's timer class
from utils import percent, direct_percent, log # custom functions that I'll pass below for clarity
import __main__ # main application
from sound import SoundPlay #custom soundplay function, which has 2 parameters. the sound instance, and the sound to play
from speak import speak # speak function


bulletspeed = 20

class bullet(object):
def __init__(self, x, y, z, range, damage, speed):
  self.x = x
  self.y= y
  self.z = z
  self.range = range
  self.damage = damage
  self.distance = 0
  self.speedtime = timer()
  self.speed = speed
  self.loop()
  log("bullet started at {}, {}, {}. it has {} tiles range".format(self.x, self.y, self.z, self.range))

def loop(self):
  log("loop started")
  if self.speedtime.elapsed>= (bulletspeed-percent(self.speed, percentage=10)):
   self.speedtime.restart()
   self.y+=1
   self.distance+=1
   self.check(self.x, self.y, self.z)
   log("sound should play")
   SoundPlay(__main__.fight, "Air Move")
   if self.distance >= self.range:
    del self
    log("bullet deleted because passed range and nothing was found")
   for target in __main__.targets:
    if self.is_in_range(target):
     log("checking range...")
     SoundPlay(__main__.fight, "Punch Hit 1")
     del self
     log("bullet deleted because nothing in range")
     return

def is_in_range(self, target):
  if target.x == self.x and target.y-self.y <= self.range and target.z == self.z:
   log("target in range")
   return True
  log("nothing found.")
  return False

utils.py

import time

localtime = time.asctime(time.localtime(time.time()))

def percent(number, percentage):
p = int(number*percentage/100)
return p
def direct_percent(number, percentage=0):
p = int((number*(100-percent(number, percentage=percentage)))/100)
return p

def log(message):
with open("game.log", "a") as f:
  f.write("{}: {}\n".format(message, localtime))

If you want to get in touch with me you can follow me on Twitter
have a nice day.
Paul

2018-06-15 07:03:29 (edited by kianoosh 2018-06-15 07:06:37)

Hi there. Could someone tell me how to detect collisions based on the players direction please? For example i'm facing northeast and standing on 0 0. My hight is 1 so my far left side would be on -1 x, 1 y and my far right side would be on -2 x and -1 y. Could someone give me a formula or an idea to do this? Although i'm not coding in python but i can port it to the language i'm coding because i have a little knowledge of python. Thanks a lot
edit: I don't want to make an arrow of vectors because i think in a fast-moving object like bullet calling a fiew for loops every time it moves makes lag or increases cpu usage a lot

Add me on skype: kianoosh.shakeri2
Or follow me on twitter @kianoosh shakeri

2018-06-15 08:58:32

Unless you're working with polygons or similarly complex shapes, you wouldn't need an array. It's faster to use unit vectors instead of the radian/degree value of theta, if you can rotate freely instead of just to the cardinal directions, but if you're not using a library that already handles that, rotation gets a little complicated. I'd still say unit vectors, because theta puts the slow part (constant trig calls) in every frame, while unit vectors put the heaviest part (half a dozen adds/muls) only in the moment of turning. The advantage is that the distance an object travels is velocity*direction*time, and if you find you need to move things based on those more for certain collision detections, this saves both CPU cycles and typing.

Anyway, collisions.

Rectangles: if you have a corner and dimensions, see Magurp's explanation above. If you have the center, then there are two ways, depending on if you have the width/height, or half the width/height. (half is faster.)
return abs(r1.x-r2.x)*2<=r1.width+r2.width and abs(r1.y-r2.y)<=r1.height+r2.height;
Or with the halfwidth/halfheight:
abs(r1.x-r2.x) <= r1.width+r2.width and abs(r1.y-r2.y) <= r1.height+r2.height;

For circles:
d=vector(c1.x-c2.x, c1.y-c2.y)
return (d.x*d.x + d.y*d.y) <= power(c1.radius+c2.radius, 2)
If you have small objects moving fast enough, they could easily move farther than their size in a single frame. This is most likely to happen with bullets. You could try to move them smaller increments, several times a frame, but that's pretty slow, especially if there are many bullets active at the same time. The quickest way would be to define the line segment that the bullet travels, then determine if any of the targets are within the bullet's width of that segment, but the math is more complicated and I don't remember it off the top of my head.

Some of my games
Keep up to date by following @Jeqofire on twitter!
Ear Ninja?

Thumbs up

2018-06-15 10:51:23 (edited by kianoosh 2018-06-15 10:57:32)

Thank you it is the thing i wanted but for rectangles how is it if they have a direction? Like they are turned 45 degrees to the left or to the right?
edit: I think There's no way except using shapes like what you wrote in bgt that we can create shapes and transforms and transform the shape with that transform we declared. The problem is that i'm coding with C# and i think converting that include to C# code will be super tuff although i think it worth the time. Although i just have read your examples i didn't use them in a game myself. Do they work as they expected and btw isn't another way to do that without shapes and just variables? I dout if there will be but just trying my chance and asking smile

Add me on skype: kianoosh.shakeri2
Or follow me on twitter @kianoosh shakeri

2018-06-15 11:54:24

Do you have to use rectangles, or would circles work? With circles, they stay the same when rotated, so that is no longer an issue.
I think C# is one of the closest languages to BGT, syntax-wise, and my 2d/3d scripts don't use much that's BGT-Specific, so I'd imagine porting would be easy. The only things I'd worry about are object creation, handles, vectors, and whether or not C++ requires declaring public/private/protected (that last one would be most of the work, I think).

Some of my games
Keep up to date by following @Jeqofire on twitter!
Ear Ninja?

Thumbs up

2018-06-15 17:55:09

I think i should use rectangles for the player and sirkels for bullets. I mean the code is a lot that is what takes time to be converted. I've already have converted some codes into C# including a map system and sam tupy's rotation pack so  i think some of the work is done already

Add me on skype: kianoosh.shakeri2
Or follow me on twitter @kianoosh shakeri