Finding vector given 2 points

Finding vector given 2 points

Postby ChristianCareaga » Thu Jun 27, 2013 12:52 am

Im helping my friend out with a pygame but we are stuck

so were are trying to get the direction of a projectile but we cant find out how

for example:

    [1,1] will go SE

    [1,-1] will go NE

    [-1,-1] will go NW

    and [-1,1] will go SW

we need an equation of some sort that will take the player pos and the mouse pos and find which direction the projectile needs to go

here is where we are plugging in the vectors:

Code: Select all
 def update(self):

    self.rect.x += self.vector[0]
    self.rect.y += self.vector[1]

then we are blitting the projectile at the rects coords

if you want to see all the code here is the repo

https://github.com/linkey11/Necromonster
ChristianCareaga
 
Posts: 52
Joined: Sat Jun 22, 2013 9:54 am

Re: Finding vector given 2 points

Postby Mekire » Thu Jun 27, 2013 2:59 am

Your vector will essentially be:
Code: Select all
offset = (self.rect.centerx-mouse[0],self.rect.centery-mouse[1])
You could then use math.atan2 to find the angle.

One important thing you should know about Rects is that they can only hold ints. This means that if your angle demands that an object move 0.5 pixels per frame, it won't move. You need to keep track of the fractional bits somehow. Depending on what you are doing you may also need to do work so that your images centers don't shift when rotating.

Here is an earlier example of mine reworked to follow and shoot with mouse.
This is the spritesheet (no need to download though as it will load using urllib)
Image

This is possibly more complicated than you need but maybe you can get the idea:
Code: Select all
import pygame as pg
import sys,os,math

class Turret(object):
    def __init__(self,loc):
        self.orig_barrel = TURRET.subsurface((0,0,150,150))
        self.barrel = self.orig_barrel.copy()
        self.base   = TURRET.subsurface((300,0,150,150))
        self.rect = self.barrel.get_rect(center=loc)
        self.base_rect = self.rect.copy()
        self.angle = self.get_angle(pg.mouse.get_pos())
    def event_manager(self,event,Objects):
        if event.type == pg.MOUSEBUTTONDOWN:
            if event.button == 1:
                Objects.append(Laser(self.rect.center,self.angle))
        elif event.type == pg.MOUSEMOTION:
            self.get_angle(event.pos)
    def update(self,Surf):
        Surf.blit(self.base,self.base_rect)
        Surf.blit(self.barrel,self.rect)
    def get_angle(self,mouse):
        offset = (self.rect.centerx-mouse[0],self.rect.centery-mouse[1])
        self.angle = math.degrees(math.atan2(*offset))-135
        oldcenter = self.rect.center
        self.barrel = pg.transform.rotate(self.orig_barrel,self.angle)
        self.rect = self.barrel.get_rect(center=oldcenter)

class Laser(object):
    def __init__(self,loc,angle):
        self.orig_laser = TURRET.subsurface((150,0,150,150))
        self.angle = -math.radians(angle-135)
        self.image = pg.transform.rotate(self.orig_laser,angle)
        self.rect = self.image.get_rect(center=loc)
        self.move = [self.rect.x,self.rect.y]
        self.speed_mag = 5
        self.speed = (self.speed_mag*math.cos(self.angle),
                      self.speed_mag*math.sin(self.angle))
        self.done = False
    def update(self,Surf):
        self.move[0] += self.speed[0]
        self.move[1] += self.speed[1]
        self.rect.topleft = self.move
        self.remove(Surf)
        Surf.blit(self.image,self.rect)
    def remove(self,Surf):
        if not self.rect.colliderect(Surf.get_rect()):
            self.done = True

class Control(object):
    def __init__(self):
        self.Screen = pg.display.get_surface()
        self.done = False
        self.Clock = pg.time.Clock()
        self.fps = 60
        self.Cannon = Turret((250,250))
        self.Objects = []
    def event_loop(self):
        keys = pg.key.get_pressed()
        for event in pg.event.get():
            if event.type == pg.QUIT or keys[pg.K_ESCAPE]:
                self.done = True
            self.Cannon.event_manager(event,self.Objects)
    def update(self):
        self.Screen.fill((50,50,50))
        self.Cannon.update(self.Screen)
        for Obj in self.Objects[:]:
            Obj.update(self.Screen)
            if Obj.done:
                self.Objects.remove(Obj)
    def main(self):
        while not self.done:
            self.event_loop()
            self.update()
            pg.display.flip()
            self.Clock.tick(self.fps)

#######
def image_from_url(url):
    try:
        from urllib2 import urlopen
        from cStringIO import StringIO as inout
    except ImportError:
        from urllib.request import urlopen
        from io import BytesIO as inout
    myurl = urlopen(url)
    return inout(myurl.read()) #Can be loaded by pygame.image.load

#######
if __name__ == "__main__":
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pg.init()
    SCREENSIZE = (500,500)
    pg.display.set_mode(SCREENSIZE)
    TURRET_URL = "http://i1192.photobucket.com/albums/aa340/Mekire/turret.png"
    TURRET = pg.image.load(image_from_url(TURRET_URL)).convert()
    TURRET.set_colorkey((255,0,255))

    RunIt = Control()
    RunIt.main()
    pg.quit();sys.exit()

-Mek

Edit:
If you have no need to rotate the images and don't need to actually know the angle, you could just find the unit vector in the appropriate direction. You do this by dividing each component of the vector by the magnitude of the vector:
Code: Select all
offset = (mouse[0]-self.rect.centerx,mouse[1]-self.rect.centery)
self.unit = (offset[0]/math.hypot(*offset),offset[1]/math.hypot(*offset))
Then the speed components of your projectile are just the components of this unit vector multiplied by the desired radial speed.
Code: Select all
self.speed = (self.speed_mag*unit_vector[0],self.speed_mag*unit_vector[1])
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Finding vector given 2 points

Postby ChristianCareaga » Thu Jun 27, 2013 5:36 am

Mekire wrote:Your vector will essentially be:
Code: Select all
offset = (self.rect.centerx-mouse[0],self.rect.centery-mouse[1])
You could then use math.atan2 to find the angle.

One important thing you should know about Rects is that they can only hold ints. This means that if your angle demands that an object move 0.5 pixels per frame, it won't move. You need to keep track of the fractional bits somehow. Depending on what you are doing you may also need to do work so that your images centers don't shift when rotating.

Here is an earlier example of mine reworked to follow and shoot with mouse.
This is the spritesheet (no need to download though as it will load using urllib)
Image

This is possibly more complicated than you need but maybe you can get the idea:
Code: Select all
import pygame as pg
import sys,os,math

class Turret(object):
    def __init__(self,loc):
        self.orig_barrel = TURRET.subsurface((0,0,150,150))
        self.barrel = self.orig_barrel.copy()
        self.base   = TURRET.subsurface((300,0,150,150))
        self.rect = self.barrel.get_rect(center=loc)
        self.base_rect = self.rect.copy()
        self.angle = self.get_angle(pg.mouse.get_pos())
    def event_manager(self,event,Objects):
        if event.type == pg.MOUSEBUTTONDOWN:
            if event.button == 1:
                Objects.append(Laser(self.rect.center,self.angle))
        elif event.type == pg.MOUSEMOTION:
            self.get_angle(event.pos)
    def update(self,Surf):
        Surf.blit(self.base,self.base_rect)
        Surf.blit(self.barrel,self.rect)
    def get_angle(self,mouse):
        offset = (self.rect.centerx-mouse[0],self.rect.centery-mouse[1])
        self.angle = math.degrees(math.atan2(*offset))-135
        oldcenter = self.rect.center
        self.barrel = pg.transform.rotate(self.orig_barrel,self.angle)
        self.rect = self.barrel.get_rect(center=oldcenter)

class Laser(object):
    def __init__(self,loc,angle):
        self.orig_laser = TURRET.subsurface((150,0,150,150))
        self.angle = -math.radians(angle-135)
        self.image = pg.transform.rotate(self.orig_laser,angle)
        self.rect = self.image.get_rect(center=loc)
        self.move = [self.rect.x,self.rect.y]
        self.speed_mag = 5
        self.speed = (self.speed_mag*math.cos(self.angle),
                      self.speed_mag*math.sin(self.angle))
        self.done = False
    def update(self,Surf):
        self.move[0] += self.speed[0]
        self.move[1] += self.speed[1]
        self.rect.topleft = self.move
        self.remove(Surf)
        Surf.blit(self.image,self.rect)
    def remove(self,Surf):
        if not self.rect.colliderect(Surf.get_rect()):
            self.done = True

class Control(object):
    def __init__(self):
        self.Screen = pg.display.get_surface()
        self.done = False
        self.Clock = pg.time.Clock()
        self.fps = 60
        self.Cannon = Turret((250,250))
        self.Objects = []
    def event_loop(self):
        keys = pg.key.get_pressed()
        for event in pg.event.get():
            if event.type == pg.QUIT or keys[pg.K_ESCAPE]:
                self.done = True
            self.Cannon.event_manager(event,self.Objects)
    def update(self):
        self.Screen.fill((50,50,50))
        self.Cannon.update(self.Screen)
        for Obj in self.Objects[:]:
            Obj.update(self.Screen)
            if Obj.done:
                self.Objects.remove(Obj)
    def main(self):
        while not self.done:
            self.event_loop()
            self.update()
            pg.display.flip()
            self.Clock.tick(self.fps)

#######
def image_from_url(url):
    try:
        from urllib2 import urlopen
        from cStringIO import StringIO as inout
    except ImportError:
        from urllib.request import urlopen
        from io import BytesIO as inout
    myurl = urlopen(url)
    return inout(myurl.read()) #Can be loaded by pygame.image.load

#######
if __name__ == "__main__":
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pg.init()
    SCREENSIZE = (500,500)
    pg.display.set_mode(SCREENSIZE)
    TURRET_URL = "http://i1192.photobucket.com/albums/aa340/Mekire/turret.png"
    TURRET = pg.image.load(image_from_url(TURRET_URL)).convert()
    TURRET.set_colorkey((255,0,255))

    RunIt = Control()
    RunIt.main()
    pg.quit();sys.exit()

-Mek

Edit:
If you have no need to rotate the images and don't need to actually know the angle, you could just find the unit vector in the appropriate direction. You do this by dividing each component of the vector by the magnitude of the vector:
Code: Select all
offset = (mouse[0]-self.rect.centerx,mouse[1]-self.rect.centery)
self.unit = (offset[0]/math.hypot(*offset),offset[1]/math.hypot(*offset))
Then the speed components of your projectile are just the components of this unit vector multiplied by the desired radial speed.
Code: Select all
self.speed = (self.speed_mag*unit_vector[0],self.speed_mag*unit_vector[1])



Youre the man Mekire! yeah we got it working thanks so much!
ChristianCareaga
 
Posts: 52
Joined: Sat Jun 22, 2013 9:54 am

Re: Finding vector given 2 points

Postby Mekire » Thu Jun 27, 2013 6:24 am

Looking good. I recommend the use of atan2 over atan in this case though:
Code: Select all
>>> import math
>>> help(math.atan2)
Help on built-in function atan2 in module math:

atan2(...)
    atan2(y, x)
   
    Return the arc tangent (measured in radians) of y/x.
    Unlike atan(y/x), the signs of both x and y are considered.
That will stop you needing to worry about those zero division errors.

Also math.hypot is really nice. Instead of calculating a vector magnitude like this:
Code: Select all
math.sqrt(distance[0] ** 2 + distance[1] ** 2)
you would just do this:
Code: Select all
math.hypot(distance[0],distance[1])
or better yet, this:
Code: Select all
math.hypot(*distance)

-Mek
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Finding vector given 2 points

Postby ChristianCareaga » Thu Jun 27, 2013 7:07 am

Mekire wrote:Looking good. I recommend the use of atan2 over atan in this case though:
Code: Select all
>>> import math
>>> help(math.atan2)
Help on built-in function atan2 in module math:

atan2(...)
    atan2(y, x)
   
    Return the arc tangent (measured in radians) of y/x.
    Unlike atan(y/x), the signs of both x and y are considered.
That will stop you needing to worry about those zero division errors.

Also math.hypot is really nice. Instead of calculating a vector magnitude like this:
Code: Select all
math.sqrt(distance[0] ** 2 + distance[1] ** 2)
you would just do this:
Code: Select all
math.hypot(distance[0],distance[1])
or better yet, this:
Code: Select all
math.hypot(*distance)

-Mek

Yup! all that works thanks Mekire!
ChristianCareaga
 
Posts: 52
Joined: Sat Jun 22, 2013 9:54 am


Return to Game Development

Who is online

Users browsing this forum: No registered users and 2 guests

cron