简体   繁体   中英

How to make a turtle move towards another turtle's positions?

I want to create the SpaceInvaders game but instead of the enemies shooting down, it will shoot towards the player. I implemented it using the .goto() method like this:

bullet2.goto(player.xcor(),player.ycor())

But the problem here is that the bullet fixes it's destination in terms of the coordinates of the player, and thus get stuck there. I want them to go on moving in that direction until it goes of the screen after when the enemy can shoot again (I didn't find a direct way to calculate the heading between 2 turtle positions).

I am also unabvle to figure out where to put the bullet2.goto(player.xcor(),player.ycor()) command: it should be in the loop of while(True) or in the function that fires the bullet.

I am posting my code below. Here is what my objective was: have 2 different type of enemies, one moving in circles while the other moves in a square pattern. The first enemy fires after making 4 moves wand the second enemy fires after making 2 moves. And everything they fire, the bullet moves towards the player and unless the bullet goes out of the screen, the enemy cannot fire again.

I am not looking for collisions.

I know a lot can be improved in terms of making everything object oriented, but right now I am focused on the functionalities of the game. Will do that once I clear the basic functions.

# python 2.7 and turtle library

import os
import random
import sys
import turtle
turtle.fd(0)
turtle.speed(6)
turtle.bgcolor("black")
turtle.ht()
turtle.setundobuffer(1)
turtle.tracer(1)

class Game():       
    def draw_border(self):
        #Draw border
        self.pen = turtle.Turtle()
        self.pen.speed(0)
        self.pen.color("white")
        self.pen.pensize(3)
        self.pen.penup()
        self.pen.goto(-300, 300)
        self.pen.pendown()
        for side in range(4):
            self.pen.fd(600)
            self.pen.rt(90)
        self.pen.penup()
        self.pen.ht()

game = Game()
game.draw_border()

bulletstate1 = "ready"
bulletstate2 = "ready"

def enemy1_fire():
    #the bullet will travel up from the player's position
    global bulletstate1 # enable modifying global var from inside of function
    if bulletstate1 == "ready":
        bulletstate1 = "fired"
        bullet1.showturtle()
        x = enemy1.xcor() # get the coordinates at the time of firing
        y = enemy1.ycor()
        bullet1.speed = 6
        bullet1.setposition(x,y) # bullet will appear just above the player
        #bullet1.goto(player.xcor(),player.ycor())
        #print(bulletspeed,bullet.xcor(),bullet.ycor(),bulletstate1)

def enemy2_fire():
    #the bullet will travel up from the player's position
    global bulletstate2 # enable modifying global var from inside of function
    if bulletstate2 == "ready":
        bulletstate2 = "fired"
        bullet2.showturtle()
        x = enemy2.xcor()
        y = enemy2.ycor()
        bullet2.speed = 6
        bullet2.setposition(x,y) # bullet will appear just above the player
        #bullet2.goto(player.xcor(),player.ycor())
        #print(bulletspeed,bullet.xcor(),bullet.ycor(),bulletstate2)

class Player(turtle.Turtle):
    def __init__(self, spriteshape, color, startx, starty):
        turtle.Turtle.__init__(self, shape = spriteshape)
        self.speed(3)
        self.penup()
        self.color(color)
        self.fd(0)
        self.goto(startx, starty)
        self.speed = 1
        self.left(90)
        #self.mode("logo")  

    def move(self):
        self.fd(self.speed)
        if (player.xcor()<-280): # boundary checking
            player.setx(-280)
        if (player.xcor()> 280): # boundary checking
            player.setx(280)
        if (player.ycor()<-280): # boundary checking
            player.sety(-280)
        if (player.ycor()> 280): # boundary checking
            player.sety(280)

    def turn_left(self):
        self.move()
        self.lt(30)
        if (player.xcor()<-280): # boundary checking
            player.setx(-280)
        if (player.xcor()> 280): # boundary checking
            player.setx(280)
        if (player.ycor()<-280): # boundary checking
            player.sety(-280)
        if (player.ycor()> 280): # boundary checking
            player.sety(280)

    def turn_right(self):
        self.move()
        self.rt(30)
        if (player.xcor()<-280): # boundary checking
            player.setx(-280)
        if (player.xcor()> 280): # boundary checking
            player.setx(280)
        if (player.ycor()<-280): # boundary checking
            player.sety(-280)
        if (player.ycor()> 280): # boundary checking
            player.sety(280)

    def accelerate(self):
        self.move()
        self.speed = self.speed + 1
        if (player.xcor()<-280): # boundary checking
            player.setx(-280)
        if (player.xcor()> 280): # boundary checking
            player.setx(280)
        if (player.ycor()<-280): # boundary checking
            player.sety(-280)
        if (player.ycor()> 280): # boundary checking
            player.sety(280)

    def brake(self):
        self.speed = self.speed - 1
        if (player.xcor()<-280): # boundary checking
            player.setx(-280)
        if (player.xcor()> 280): # boundary checking
            player.setx(280)
        if (player.ycor()<-280): # boundary checking
            player.sety(-280)
        if (player.ycor()> 280): # boundary checking
            player.sety(280)

class Enemy1(turtle.Turtle):
    def __init__(self, spriteshape, color, startx, starty):
        turtle.Turtle.__init__(self, shape = spriteshape)
        self.speed(3) #animation speed
        self.penup()
        self.color(color)
        self.fd(0)
        self.goto(startx, starty)
        self.speed = 1
        #self.mode("logo")

    shoot = 4 # shoots after 4 interval

    def move(self):
        self.lt(90)
        self.fd(150)
        self.shoot = self.shoot - 1
        if self.shoot==0:
            enemy1_fire() #shoot below, better if can be directed at player
            self.shoot = 4

class Enemy2(turtle.Turtle):
    def __init__(self, spriteshape, color, startx, starty):
        turtle.Turtle.__init__(self, shape = spriteshape)
        self.speed(3)
        self.penup()
        self.color(color)
        self.fd(0)
        self.goto(startx, starty)
        self.speed = 1
        #self.mode("logo")

    shoot = 2 # shoots after 2 interval

    def move(self):
        self.fd(100)
        self.rt(30)
        self.shoot= self.shoot-1
        if self.shoot==0:
            enemy2_fire() #shoot towards player
            self.shoot = 2

enemy1 = Enemy1("circle", "red", 50, -50)
enemy2 = Enemy2("square", "blue", -10, 200)
player = Player("triangle", "white", 0, 0)


#key bindings
turtle.listen()
turtle.onkey(player.turn_left,"Left")
turtle.onkey(player.turn_right,"Right")
turtle.onkey(player.accelerate,"Up")
turtle.onkey(player.brake,"Down")

# create a bullet for the enemy1
bullet1  = turtle.Turtle()
bullet1.color("yellow")
bullet1.shape("triangle")
bullet1.penup()
bullet1.shapesize(0.3,0.3) # length and breadth of bullet
bullet1.hideturtle()
bullet1.speed(3)
bullet1.speed = 2

# create a bullet for the enemy2
bullet2  = turtle.Turtle()
bullet2.color("yellow")
bullet2.shape("square")
bullet2.penup()
bullet2.shapesize(0.4,0.4) # length and breadth of bullet
bullet2.hideturtle()
bullet2.speed(3)
bullet2.speed = 2


while True:
    enemy1.move()
    enemy2.move()

    if bulletstate1=="fired":
        # y = bullet1.ycor()
        # y = y - bullet1.speed
        # bullet1.sety(y)
        bullet1.goto(player.xcor(),player.ycor())

    if bulletstate2=="fired":
        # y = bullet2.ycor()
        # y = y - bullet2.speed
        # bullet2.sety(y)
        bullet2.goto(player.xcor(),player.ycor())

    if (bullet1.ycor()>275 or bullet1.xcor()>275 or bullet1.ycor()<-275 or bullet1.xcor()<-275):
        bullet1.hideturtle()
        # bullet1.sety(enemy1.ycor)
        # bullet1.setx(enemy1.xcor)
        bulletstate1="ready"


    if (bullet2.ycor()>275 or bullet2.xcor()>275 or bullet2.ycor()<-275 or bullet2.xcor()<-275):
        bullet2.hideturtle()
        # bullet2.sety(enemy2.ycor)
        # bullet2.setx(enemy2.xcor)
        bulletstate2="ready"

sys.stdout.close()
delay = raw_input("Press enter to finish. > ")

If you are still interested in solving this problem, you should solve the right triangles (see the figure):

        px            
---------|--x--------|
|        |  |        |
|        |  x        |
y--------|-[e]y------y
|        |/ |        |
py-------*py--------py
|       /px |        |
|      / |  |        |
|     /  |  |        |
|    /   |  |        |
|---v----|--x--------|
  y2=-280
  x2=?

Let [e] be the enemy, and * be the player, x and y be the enemy's coordinates, px and py be the player's coordinates. For the case shown, you should get the x2 coordinate from the relations:
(x-px)/(y-py) = (x-x2)/(y-(-280)) => (x-x2) = (y-(-280))*(x-px)/(y-py) =>
=> x2 = x-(y-(-280))*(x-px)/(y-py) ,
where x , y , px , py and y2 are known.

And you must take into account the relative position of the player and the enemy, for example:

               px
       ---------|------x----|
       |        |      |    |
       |        |      |    |
       |        |      x    |
       y--------|-- . [e]y--y
       py-------*py----|---py
       |    .   px     |    |
       |  .     |      |    |
y2=?   <. ------|------|----|
x2=-280|        |      |    |
       |--------|------x----|

(x-px)/(y-py) = (x-(-280))/(y-y2) => (y-y2) = (x-(-280))*(y-py)/(x-px) =>
=> y2 = y-(x-(-280))*(y-py)/(x-px) ,
and the signs of the differences in coordinates.
This leads us, in your case, to the following code (I advise you to design it as a separate function):

MAX_COOR=330

def bullet_calc(player,x,y):
  diff_x=x-player.xcor()
  diff_y=y-player.ycor()
  if diff_y==0:
    goal_y=y
    if diff_x>0:
      goal_x=-MAX_COOR
    elif diff_x<=0:
      goal_x=MAX_COOR

  elif diff_x==0:
    goal_x=x
    if diff_y>0:
      goal_y=-MAX_COOR
    elif diff_y<=0:
      goal_y=MAX_COOR

  elif diff_x>0 and diff_y>0 and abs(diff_x)<abs(diff_y):  #    |-    - enemy
      goal_x=x-(y-(-MAX_COOR))*diff_x/diff_y               # ---*---  * player
      goal_y=-MAX_COOR                                     #    |

  elif diff_x>0 and diff_y>0 and abs(diff_x)>abs(diff_y):  #    |  _
      goal_x=-MAX_COOR                                     # ---*---
      goal_y=y-(x-(-MAX_COOR))*diff_y/diff_x               #    |

  elif diff_x<0 and diff_y>0 and abs(diff_x)<abs(diff_y):  #   -|    
      goal_x=(y-MAX_COOR)*diff_x/diff_y-x                  # ---*---
      goal_y=-MAX_COOR                                     #    |

  elif diff_x<0 and diff_y>0 and abs(diff_x)>abs(diff_y):  # _  | 
      goal_x=MAX_COOR                                      # ---*---
      goal_y=(x-(-MAX_COOR))*diff_y/diff_x-y               #    |

  elif diff_x>0 and diff_y<0 and abs(diff_x)<abs(diff_y):  #    | 
      goal_x=(y-(-MAX_COOR))*diff_x/diff_y-x               # ---*---
      goal_y=MAX_COOR                                      #    |_

  elif diff_x>0 and diff_y<0 and abs(diff_x)>abs(diff_y):  #    | 
      goal_x=-MAX_COOR                                     # ---*--_
      goal_y=(x-MAX_COOR)*diff_y/diff_x-y                  #    |

  elif diff_x<0 and diff_y<0 and abs(diff_x)<abs(diff_y):  #    | 
      goal_x=x-(y-MAX_COOR)*diff_x/diff_y                  # ---*---
      goal_y=MAX_COOR                                      #   _|

  elif diff_x<0 and diff_y<0 and abs(diff_x)>abs(diff_y):  #    | 
      goal_x=MAX_COOR                                      # _--*---
      goal_y=y-(x-MAX_COOR)*diff_y/diff_x                  #    |

  return (goal_x,goal_y)

And it seems easier to call this code in the function that fires the bullet:

def enemy1_fire():
    #the bullet will travel up from the player's position
    global bulletstate1 # enable modifying global var from inside of function
    if bulletstate1 == "ready":
        bulletstate1 = "fired"
        x = enemy1.xcor() # get the coordinates at the time of firing
        y = enemy1.ycor()
        bullet1.speed = 6
        bullet1.setposition(x,y) # bullet will appear just above the player
        bullet1.showturtle()     # move <showturtle()> here
        bullet1.goto(bullet_calc(player,x,y))
        #bullet1.goto(player.xcor(), player.ycor())  # your old code

All this is easier if the player is in the center of the field.

Instead of doing:

if bulletstate1=="fired":
    bullet1.goto(player.xcor(),player.ycor())

what you want is:

if bulletstate1 == 'fired':
    bullet1.setheading(bullet1.towards(player))
    bullet1.forward(min(10, bullet1.distance(player))

That is in your motion loop, on each iteration have the bullet turn towards the player's current position and move forward slightly. If the player is standing still, the bullet will eventually hit them. If the player is moving, the bullet will track them.

However, the bullet won't likely "go off the screen" . Possibly if the step forward is very large:

 bullet1.forward(min(100, bullet1.distance(player))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM