January 15, 2011

Artillery Game

This is a basic artillery game I wrote in python.  There are still some minor issues I need to fix, but it works and is playable.  It includes wind, movement, and variable-height (but not shape) terrain.




Here is the code:



from tkinter import *
from sys import getfilesystemencoding
import time
import random
import math

height = 600
width = 800
global t, Vo, P1Vo, P2Vo, P1A, P2A, counter,wind,active
Vo=0
t=0
Angle=0
counter=2
P1Vo=0
P1A=0
P2Vo=0
P2A=0
wind=0
active=False

##

def windspeed():
   global wind
   x=0
   for i in range(4):
      x+=random.randint(-20,20) 
   wind=x//4
   return wind

canvas = Canvas(highlightthickness=0, height=height, width=width, )
canvas.master.title("Artillery")
if getfilesystemencoding() == 'utf-8':       # only for mac
   canvas.master.call('console', 'hide')
canvas.pack()

canvas.create_rectangle(0,600,800,590,fill='green', outline='green', tag = 'ground')



class missile():
   "blows junk up"
   def __init__(self,power=Vo,angle=Angle, start=0,y=590, direction='right'):
      global t

      self.power=power
      self.angle=angle
      self.x=start
      self.y=y
      self.d=direction
      self.id = canvas.create_oval(self.x,self.y,self.x+5,self.y-5,fill='red')
   def trajectory(self,start, starty):
      "calculates the position of the missile"
      global t
      #canvas.coords(self.id,self.x,self.y,self.x+5,self.y-5)
      #canvas.create_oval(self.x,self.y,self.x+1,self.y-1)
      if self.d=='right':
         self.x=int((self.power*t*math.cos(self.angle))//1)+start
      else:
         self.x=int(-((self.power*t*math.cos(self.angle))//1))+start
      self.y=int((-(((-9.8*(t**2))//2) + (self.power*math.sin(self.angle)*t)//1))+starty)
      canvas.coords(self.id,self.x,self.y,self.x+5,self.y-5)

      t+=.005
      
class tank():
   "shoots some missiles"
   def __init__(self, pos=50,health=100,movement=100, side=10,):
      self.pos=pos
      self.side=side
      self.health=health
      self.pic=canvas.create_rectangle(self.pos, 590, self.pos+20, 580)
      self.display_health()
      self.movement=movement
      self.display=canvas.create_text(self.side+50, 50, text=(self.movement))
      self.barrel_r()
   def display_health(self):
      "displays the tank's health bar"
      self.x=canvas. create_rectangle(self.side, 20, self.side+self.health, 23, fill='red')
   def move_left(self):
      global Angle
      canvas.coords(self.pic, self.pos-2, 590, self.pos+18, 580)
      canvas.update()
      self.movement-=1
      self.pos-=2
      a_up(Angle/.0174532925)
      self.display_mov()
   def move_right(self):
      canvas.coords(self.pic, self.pos+2, 590, self.pos+22, 580)
      canvas.update()
      self.movement-=1
      self.pos+=2
      a_up(Angle/.0174532925)
      self.display_mov()
   def display_mov(self):
      "displays the amount of movement left"
      canvas.delete(self.display)
      self.display=canvas.create_text(self.side+50, 50, text=(self.movement))
   def barrel_r(self):
      global Angle
      self.b=canvas.create_line(self.pos+10, 580, 15*math.cos(Angle)+self.pos+10, 580- 15*math.sin(Angle))
      self.tip=(15*math.cos(Angle)+self.pos+10)
      self.y=(580- 15*math.sin(Angle))
      canvas.update()
   def barrel_l(self):
      global Angle
      self.b=canvas.create_line(self.pos+10, 580, 15*math.cos(Angle-(math.pi))+self.pos+10, 580- 15*math.sin(Angle))
      self.tip=(15*math.cos(Angle-(math.pi))+self.pos+10)
      self.y=(580- 15*math.sin(Angle))
      canvas.update()

class terrain():
   "a hill that is placed in the middle of the map"
   def __init__(self):
      height=random.randint(50,600)
      self.shape=canvas.create_polygon(100, 600+height, 250, 200+height, 300, 250+height,400, 0+height, 700, 600+height, fill='green')


hill=terrain()


      
wind=windspeed()
z=canvas.create_text(400,20,text=('Wind Speed: %.2d') %(wind))


tank1=tank()
tank2=tank(pos=720, side=690)

def shoot(tank, p, a):
   "animates the missile, along with lots of other stuff"
   global t,counter, P1Vo, P2Vo, P1A, P2A, Angle, Vo, tank1, tank2,wind,z,active,text, hill
   active=True
   if counter %2 ==0:
      tank=tank2
      target=tank1
   else:
      tank=tank1
      target=tank2    
   if tank==tank1:
      d='right'
   else:
      d='left'
   
   x=missile(power=p,angle=a,start=tank.tip+1, y=tank.y-3, direction=d)
   while True:
      x.x+=int((wind*t**2)/5)

      x.trajectory(start=tank.tip, starty=tank.y)

      hit=target.pos
      if (hit<= x.x <= hit+20) and 590>=x.y>=580:
         target.health-=20
         print (target.health)
         t=0
         canvas.delete(x.id)
         counter+=1
         tag=target.x
         canvas.delete(tag)
         target.display_health()
         if target.health==0:
            if target==tank1:
               player='Player 1'
            else:
               player='Player 2'
            text=canvas.create_text(400,300, text=("%s wins") % (player))
            
            canvas.update()
            break 
         break
         
      elif x.y>600 or x.x>800 or x.x<0:
         t=0
         canvas.delete(x.id)
         counter+=1
         break
      elif hill.shape in (canvas.find_overlapping(x.x, x.y, x.x+5, x.y-5)):
         canvas.delete(x.id)
         t=0
         counter+=1
         break

      time.sleep(.001):
      canvas.update()

   if counter % 2 == 0:
      p_adjuster.set(P1Vo)
      a_adjuster.set(P1A/.0174532925)

   else:
      p_adjuster.set(P2Vo)
      a_adjuster.set(P2A/.0174532925)
   wind=windspeed()
   canvas.delete(z)
   z=canvas.create_text(400,20,text=('Wind Speed: %.2d') %(wind))
   active=False




  
def keypress(event):
   global tank1, Vo, Angle, counter, P1A, P2A, P1Vo, P2Vo,active

   if not active:
      if event.keysym=="space":
         if counter % 2 == 0:
            Angle=P1A
            Vo=P1Vo
         else:
            Angle=P2A
            Vo=P2Vo

         shoot(tank1, Vo, Angle)
   if event.keysym=='Right':
      if counter %2 ==0: 
         tank=tank2
      else:
         tank=tank1
      if tank.movement > 0 and tank.pos<778:
         tank.move_right()
      else:
         pass

   elif event.keysym=='Left':
      if counter %2 ==0:
         tank=tank2
      else:
         tank=tank1
      if tank.movement > 0 and 0<tank.pos:
         tank.move_left()
      else: 
         pass

def retry(event):
   global  tank1, tank2,text,counter, P1Vo, P2Vo, P1A, P2A, hill
   counter=2
   canvas.delete(text)
   canvas.delete(tank1.pic)
   canvas.delete(tank2.pic)
   canvas.delete(tank1.b)
   canvas.delete(tank2.b)
   canvas.delete(tank1.x)
   canvas.delete(tank2.x)
   canvas.delete(tank1.display)
   canvas.delete(tank2.display)
   canvas.delete(hill.shape)
   hill=terrain()
   P1Vo, P2Vo, P1A, P2A=0, 0, 0, 0
   tank1.health=100
   tank2.health=100
   tank1=tank()
   tank2=tank(pos=720, side=690)
   p_adjuster.set(0)
   a_adjuster.set(0)



def p_up(x):
   global Vo, Angle,counter, P1Vo, P2Vo
   if counter % 2 == 0:
         P1Vo=int(x)
         Vo=P1Vo
   else:
         P2Vo=int(x)
         Vo=P2Vo
def a_up(x):
   global Vo,Angle,counter, P1A, P2A, tank1, tank2
   if counter % 2 == 0:
         P1A=int(x)*.0174532925
         Angle=P1A
         tank=tank2
         canvas.delete(tank.b)
         tank.barrel_l()
   else:
         P2A=int(x)*.0174532925
         Angle=P2A
         tank=tank1
         canvas.delete(tank.b)
         tank.barrel_r()
   


p_adjuster = Scale(canvas.master, length=400, orient=HORIZONTAL, command=p_up, from_=0, to=200, label="Power")
p_adjuster.pack(side=LEFT)
a_adjuster = Scale(canvas.master, length=400, orient=HORIZONTAL, command=a_up, from_=0, to=90, label = "Angle" )
a_adjuster.pack(side=RIGHT)
x
     
canvas.master.bind("<Key>", keypress)
canvas.master.bind("<Key-Return>", retry)


####



canvas.mainloop()




In order to run the game at a reasonable speed, it may be necessary to adjust the numbers in line 64:
"t+=.005"
or line 183:
"time.sleep(.001):"

To play, you can move your tank with the arrow keys, and press space bar to fire.  Power and angle are adjusted with the sliders.

No comments:

Post a Comment