For my programming elective, I thought I’d try to get students programming autonomous agents that we could all compile together into some type of game. Since it was, for most of them a first class in programming, this has proven a little too ambitious, and I’ve had to adjust a bit to build up to it.
Step 1: Build a spaceship/vehicle/agent.
- We’re using VPython so we can build 3d ships. Students get to exercise their creativity a bit and learn how to place things in 3d co-ordinate space.
- The ship must have a front end and all the parts must be put into a single frame so the whole thing can be moved as a unit.
- Although it’s not required, I’ve added in the beginnings of a trail so we can track the motion of the spaceship
from visual import * f = frame() r = ring(frame=f, thickness=.25, axis=(0,1,0)) cabin = sphere(frame=f, radius=0.6, color=color.red) front = pyramid(frame=f, height=1,width=1,length=2, color=color.blue) trail = curve(pos=[f.pos,])
Step 2: Make the spaceship a class.
- Putting the spaceship in a separate class, away from the rest of the code, makes it easier to move from one program to another. That means we can import everyone’s ships into a single program at the end.
from visual import * class spaceship1: def __init__(self): self.f = frame() self.r = ring(frame=self.f, thickness=.25, axis=(0,1,0)) self.cabin = sphere(frame=self.f, radius=0.6, color=color.red) self.front = pyramid(frame=self.f, height=1,width=1,length=2, color=color.blue) self.trail = curve(pos=[self.f.pos,]) ss = spaceship1()
Make the spaceship move
- We’re going to control the movement of the spaceship using the arrow keys. Left and right to turn; up and down to accelerate and decelerate.
- To allow movement, we’ll put the keyboard controls into an infinite loop, but to allow maximum flexibility (and to show how to use create methods in a class) we’re putting the actual movement in as methods in the class: the turning method is named turn_xz and movement is move_xz since all the motion is going to be restricted in the xz plane for now.
from visual import * class spaceship1: def __init__(self): self.f = frame() self.r = ring(frame=self.f, thickness=.25, axis=(0,1,0)) self.cabin = sphere(frame=self.f, radius=0.6, color=color.red) self.front = pyramid(frame=self.f, height=1,width=1,length=2, color=color.blue) self.trail = curve(pos=[self.f.pos,]) def turn_xz(self, turn_rate): self.f.axis = rotate(self.f.axis, turn_rate, (0,1,0)) def move_xz(self, speed): self.f.pos = self.f.pos + self.f.axis*speed self.trail.append(pos=self.f.pos) ss = spaceship1() turn = 0.0 speed=0.0 while 1: rate(20) #slows down execution of the loop ss.turn_xz(turn) ss.move_xz(speed) if scene.kb.keys: # event waiting to be processed? s = scene.kb.getkey() # get keyboard info if s == 'left': turn = turn + 0.01 if s == 'right': turn = turn - 0.01 if s == 'up': speed = speed + 0.01 if s == 'down': speed = speed - 0.01 if s == ' ': if turn <> 0: turn = 0 elif speed <> 0: speed = 0
Fire missiles
- We add in missiles by creating a class very similar to the spaceship. For now our missile is just a ball but I’m putting it into a frame anyway in case later on I want it to be a composite object.
- Since we can fire multiple missiles, in the code I create a list to hold all the missiles.
- Missiles are fired using the “a” key.
from visual import * class spaceship1: def __init__(self): self.f = frame() self.r = ring(frame=self.f, thickness=.25, axis=(0,1,0)) self.cabin = sphere(frame=self.f, radius=0.6, color=color.red) self.front = pyramid(frame=self.f, height=1,width=1,length=2, color=color.blue) self.trail = curve(pos=[self.f.pos,]) def turn_xz(self, turn_rate): self.f.axis = rotate(self.f.axis, turn_rate, (0,1,0)) def move_xz(self, speed): self.f.pos = self.f.pos + self.f.axis*speed self.trail.append(pos=self.f.pos) class missile: def __init__(self, axis, pos): self.f = frame(axis=axis, pos=pos) self.r = sphere(frame=self.f, radius=0.2, color=color.green) self.speed = 0.5 def move_xz(self): self.f.pos = self.f.pos + self.f.axis*self.speed ss = spaceship1() turn = 0.0 speed=0.0 missiles = [] #list for missiles while 1: rate(20) #slows down execution of the loop ss.turn_xz(turn) ss.move_xz(speed) #move missiles for i, m in enumerate(missiles): m.move_xz() if scene.kb.keys: # event waiting to be processed? s = scene.kb.getkey() # get keyboard info if s == 'left': turn = turn + 0.01 if s == 'right': turn = turn - 0.01 if s == 'up': speed = speed + 0.01 if s == 'down': speed = speed - 0.01 if s == ' ': if turn <> 0: turn = 0 elif speed <> 0: speed = 0 #fire missile if s == 'a': missiles.append(missile(ss.f.axis, ss.f.pos))
Final adjustments
- Finally, I’m going to put in a boundary, and set it up to delete the missiles if they go out of bounds.
- I’m also going to set an option so that the scene follows the ship, which makes it easier to keep track of things.
from visual import * class spaceship1: def __init__(self): self.f = frame() self.r = ring(frame=self.f, thickness=.25, axis=(0,1,0)) self.cabin = sphere(frame=self.f, radius=0.6, color=color.red) self.front = pyramid(frame=self.f, height=1,width=1,length=2, color=color.blue) self.trail = curve(pos=[self.f.pos,]) def turn_xz(self, turn_rate): self.f.axis = rotate(self.f.axis, turn_rate, (0,1,0)) def move_xz(self, speed): self.f.pos = self.f.pos + self.f.axis*speed self.trail.append(pos=self.f.pos) class missile: def __init__(self, axis, pos): self.f = frame(axis=axis, pos=pos) self.r = sphere(frame=self.f, radius=0.2, color=color.green) self.speed = 0.5 def move_xz(self): self.f.pos = self.f.pos + self.f.axis*self.speed class bounds: def __init__(self, boundary_distance): self.boundary_distance = boundary_distance self.border = curve(pos=[(-boundary_distance,0,-boundary_distance), (boundary_distance,0,-boundary_distance), (boundary_distance,0,boundary_distance), (-boundary_distance,0,boundary_distance), (-boundary_distance,0,-boundary_distance)]) def in_bounds(self, pos): if ( pos.x < -self.boundary_distance or pos.x > self.boundary_distance or pos.z < -self.boundary_distance or pos.z > self.boundary_distance): return false else: return true ss = spaceship1() turn = 0.0 speed=0.0 l_track_ship = 1 scene.range=10 scene.forward = (0,-1,0) missiles = [] boundary_distance = 100 boundary = bounds(boundary_distance) while 1: rate(20) if l_track_ship > 0: scene.center = ss.f.pos ss.turn_xz(turn) ss.move_xz(speed) #move missiles del_list = [] for i, m in enumerate(missiles): m.move_xz() #delete missile if out of bounds if boundary.in_bounds(m.f.pos) == False: del_list.append(i) for i in del_list: missiles[i].f.visible = False del missiles[i] #if m <> None: # m.move_xz() if scene.kb.keys: # event waiting to be processed? s = scene.kb.getkey() # get keyboard info if s == 'left': turn = turn + 0.01 if s == 'right': turn = turn - 0.01 if s == 'up': speed = speed + 0.01 if s == 'down': speed = speed - 0.01 if s == ' ': if turn <> 0: turn = 0 elif speed <> 0: speed = 0 #fire missile if s == 'a': missiles.append(missile(ss.f.axis, ss.f.pos))
Next steps
Now that we’re cooking with charcoal — we have a functioning program — the next steps will be setting up a target to shoot at, and, if the students are ready, letting them program their ships to move autonomously. If they’re not ready then, as an example, I’ll program some opposition.