Hi, Bob,
As Noel said, there are many ways to do what you want.
The attached program uses a class abstraction to handle events, and two
sprite types for the game objects. These are extremely basic. They may
not fit all your use cases. But the more general purpose you try to get,
the more elaborate your abstractions must become--and less ideal for
instructing. I hope you find a balance that helps your class.
Collision detection is easy enough for folks to grasp. Collision
handling, I think, is an approach that must be grasped in concept
because it must accommodate a number of factors that reduce to one
simple question: When a couple things collide, then what should happen?
- What are the elements colliding: blobs, bullets, lasers, etc.
- Is there one moving object, or multiple to consider in each time slice?
- In what order should they be checked?
- Should they be allowed to overlap?
- Do you need to know which edges hit?
- Should the one moving be blocked; or should they both bump back and
how much?
- For more complex logic, sometimes you might want an arbiter to
determine the outcome of a collision.
This is the part that often is custom coded for a game, unless two games
can use the same collision system. Even a nice physics library doesn't
handle all your cases "out of the box".
The attached program uses a very basic approach to collision handling:
the moving and stationary things are blobs, and the moving thing is
blocked; also the moving thing is kept within screen bounds. I usually
find it undesirable to simply stop movement, though, as if hung up on a
sticky surface. Unless it really is a sticky surface, it is "nicer" to
slide along an available path. You'll see what I mean when you interact
with the program. I decided to include the slightly more complex logic
because I found it instructional, myself, to understand how a dumb
sprite needs to discover its vicinity and decide how to get around in it.
If you had different questions in mind than were answered so far, please
feel free to clarify.
Enjoy,
Gumm
On 10/6/2015 1:12 PM, Bob Irving wrote:
Hi folks,
We're introducing Python for 9th grade this year and moving from Blitz
Basic for game programming. Just wondering if anyone has come up with
a way to hide the complexity for keyboard movement..... it's one of
those things that makes sense to more advanced programmers but not to
beginners. We've experimented with making a few functions to detect
collisions, mouse clicks....
TIA,
Bob Irving
Porter-Gaud School
Charleston, SC
--
Twitter: @birv2
www.bob-irving.com <http://www.bob-irving.com>
http://www.scoop.it/t/on-the-digital-frontier
import pygame
from pygame.locals import *
class EventThing(object):
horiz_movement = {K_LEFT: -1, K_RIGHT: 1}
vert_movement = {K_UP: -1, K_DOWN: 1}
def __init__(self):
self.running = True
self.movex = 0
self.movey = 0
def get_vector(self):
return self.movex, self.movey
def do_event(self, e):
if e.type == KEYDOWN:
self._do_keydown(e.key)
elif e.type == KEYUP:
self._do_keyup(e.key)
elif e.type == QUIT:
self._do_quit()
def _do_keydown(self, key):
if key in self.horiz_movement.keys():
self.movex += self.horiz_movement[e.key]
elif key in self.vert_movement.keys():
self.movey += self.vert_movement[e.key]
elif key == K_ESCAPE:
self._do_quit()
def _do_keyup(self, key):
if e.key in self.horiz_movement.keys():
self.movex -= self.horiz_movement[e.key]
elif e.key in self.vert_movement.keys():
self.movey -= self.vert_movement[e.key]
def _do_quit(self):
self.running = False
class Sprite(pygame.sprite.Sprite):
def __init__(self, position, color):
self.image = pygame.Surface((60, 60))
self.image.fill(color)
self.rect = self.image.get_rect(center=position)
class MobileSprite(Sprite):
def move_among_obstacles(self, vec, obstacles, clamp_rect=None):
def test_move(try_vec):
rect = self.rect.move(try_vec)
if clamp_rect is not None and not clamp_rect.contains(rect):
rect.clamp_ip(clamp_rect)
try_vec = rect.x - self.rect.x, rect.y - self.rect.y
for obj in obstacles:
if obj.rect.colliderect(rect):
return None
return try_vec
for try_vec in vec, (vec[0], 0), (0, vec[1]):
ok_vec = test_move(try_vec)
if ok_vec is not None:
self.rect.move_ip(*ok_vec)
pygame.init()
resolution = 640, 480
screen = pygame.display.set_mode(resolution)
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
max_fps = 60
clear_color = Color('black')
mobile_color = Color('darkviolet')
stationary_color = Color('red')
# Make the moving sprite, obstacles
guy = MobileSprite(screen_rect.center, mobile_color)
rocks = []
for x in resolution[0] * 1/4, resolution[0] * 3/4:
for y in resolution[1] * 1/4, resolution[1] * 3/4:
rocks.append(Sprite((x, y), stationary_color))
# Make the event logic handler
event_thing = EventThing()
while event_thing.running:
# Time
dt = clock.tick(max_fps) / 1000.0
# Input
for e in pygame.event.get():
event_thing.do_event(e)
# Logic
vector = event_thing.get_vector()
guy.move_among_obstacles(vector, rocks, screen_rect)
# Visuals
screen.fill(clear_color)
for rock in rocks:
screen.blit(rock.image, rock.rect)
screen.blit(guy.image, guy.rect)
pygame.display.flip()