Reputation: 467
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. > ")
Upvotes: 1
Views: 1898
Reputation: 41872
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))
Upvotes: 2
Reputation: 411
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.
Upvotes: 1