Mini projects #4 ---- Pong

课程全名:An Introduction to Interactive Programming in Python,来自 Rice University

授课教授:Joe Warren, Scott Rixner, John Greiner, Stephen Wong

工具:http://www.codeskulptor.org/, simplegui 模块

 

第四次作业,完成一个弹球的游戏,游戏的界面如下,规则也很简单,球不断在两边的paddle之间撞击后速度会不断加快,直到有一名玩家无法将球回击,则另一名玩家得分。

image

之前得了解一下控制物体移动的方式。

假设物体与点p相对应,第一种方式是直接通过控制点p的坐标来实现移动

在draw_handler中不用进行更新,只用绘制他的position就可以

坐标的变化放在key_handler中进行修改,比如

key_handler:

    Left Arrow –> p[0] –= c

    Right Arrow –> p[0] += c

    Up Arrow –> p[1] –= c

    Down Arrow –> p[1] += c

代码如下:

import simplegui

WIDTH = 500
HEIGHT = 300
p = [WIDTH/2, HEIGHT/2]

def draw(canvas):
    canvas.draw_circle(p, 30, 1, "White", "White")

def key_down(key):
    c = 4
    if key == simplegui.KEY_MAP['up']:
        p[1] -= c
    elif key == simplegui.KEY_MAP['down']:
        p[1] +=c
    elif key == simplegui.KEY_MAP['left']:
        p[0] -= c
    elif key == simplegui.KEY_MAP['right']:
        p[0] += c
    
# Create a frame and assign callbacks to event handlers
frame = simplegui.create_frame("Position Control", WIDTH, HEIGHT)
frame.set_draw_handler(draw)
frame.set_keydown_handler(key_down)

# Start the frame animation
frame.start()

第二种方式通过控制点p的速率(velocity)变化来实现移动。

draw_handler中进行坐标的更新和绘制

在key_handler中进行速率的调整

draw_handler:

    p[0] += v[0]

    p[1] += v[1]

key_handler:

    Left Arrow –> v[0] –= c

    Right Arrow –> v[0] += c

    Up Arrow –> v[1] –= c

    Down Arrow –> v[1] += c

代码如下:

import simplegui

WIDTH = 500
HEIGHT = 300
v = [0, 0]
p = [WIDTH/2, HEIGHT/2]

def draw(canvas):
    p[0] += v[0]
    p[1] += v[1]
    canvas.draw_circle(p, 30, 1, "White", "White")

def key_down(key):
    c = 1
    if key == simplegui.KEY_MAP['up']:
        v[1] -= c
    elif key == simplegui.KEY_MAP['down']:
        v[1] +=c
    elif key == simplegui.KEY_MAP['left']:
        v[0] -= c
    elif key == simplegui.KEY_MAP['right']:
        v[0] += c
    
# Create a frame and assign callbacks to event handlers
frame = simplegui.create_frame("Position Control", WIDTH, HEIGHT)
frame.set_draw_handler(draw)
frame.set_keydown_handler(key_down)

# Start the frame animation
frame.start()

这里再提一下碰撞的处理,上下碰撞,只用把v[1]的方向取反,左右碰撞,把v[0]的方向取反。

主要就是边界位置的判断,对于一个假定中心为p,半径为r的求来说:

碰撞如下:

Left Wall:

p[0] <= r

Right Wall:

p[0] >= (width-1)-r

Top Wall:

p[1] <= r

Bottom Wall:

p[1] >= (height-1)-r

下面两张图来自老师的课件,关于定义

image

image

回归到这个游戏,基本要用到的知识也就这么多,移动paddle处理方式稍微特殊一点,keydown_handler进行velocity的正方向增加,那么keyup_handler进行velocity的负方向增加,这样就可以实现,按下按键后paddle持续移动,释放按键后paddle停止移动。

对于小球,初始随机给一个velocity,只要不是垂直或者水平,然后处理碰撞,在进行paddle碰撞后,要给予小球10%velocity上的增量。

完整代码如下:

# Implementation of classic arcade game Pong

import simplegui
import random

# initialize globals - pos and vel encode vertical info for paddles
WIDTH = 600
HEIGHT = 400       
BALL_RADIUS = 20
PAD_WIDTH = 8
PAD_HEIGHT = 80
HALF_PAD_WIDTH = PAD_WIDTH / 2
HALF_PAD_HEIGHT = PAD_HEIGHT / 2
LEFT = False
RIGHT = True

# initialize ball_pos and ball_vel for new bal in middle of table
# if direction is RIGHT, the ball's velocity is upper right, else upper left
def spawn_ball(direction):
    global ball_pos, ball_vel # these are vectors stored as lists
    ball_pos = [WIDTH/2, HEIGHT/2]
    ball_dir = 1 if direction == RIGHT else -1
    ball_vel = [ball_dir * random.randrange(2, 4), -random.randrange(1, 3)]

# define event handlers
def new_game():
    global paddle1_pos, paddle2_pos, paddle1_vel, paddle2_vel  # these are numbers
    global score1, score2  # these are ints
    spawn_ball(random.choice([LEFT, RIGHT]))
    paddle1_pos, paddle2_pos = HEIGHT / 2, HEIGHT / 2
    paddle1_vel, paddle2_vel = 0, 0
    score1, score2 = 0, 0
    
    
def draw(canvas):
    global score1, score2, paddle1_pos, paddle2_pos, ball_pos, ball_vel
 
    # draw mid line and gutters
    canvas.draw_line([WIDTH / 2, 0],[WIDTH / 2, HEIGHT], 1, "White")
    canvas.draw_line([PAD_WIDTH, 0],[PAD_WIDTH, HEIGHT], 1, "White")
    canvas.draw_line([WIDTH - PAD_WIDTH, 0],[WIDTH - PAD_WIDTH, HEIGHT], 1, "White")
        
    # update ball
    new_ball_posX = ball_pos[0] + ball_vel[0]
    new_ball_posY = ball_pos[1] + ball_vel[1]
    if new_ball_posY <= BALL_RADIUS:
        ball_pos[1] = BALL_RADIUS
        ball_vel[1] *= -1
    elif new_ball_posY >= HEIGHT - 1 - BALL_RADIUS:
        ball_pos[1] = HEIGHT - 1 - BALL_RADIUS
        ball_vel[1] *= -1
    else:
        ball_pos[1] = new_ball_posY
        
    # hit the left gutter
    if new_ball_posX <= PAD_WIDTH + BALL_RADIUS:
        ball_pos[0] = PAD_WIDTH + BALL_RADIUS
        if (ball_pos[1] >= paddle1_pos - HALF_PAD_HEIGHT 
            and ball_pos[1] <= paddle1_pos + HALF_PAD_HEIGHT):
            ball_vel[0] = -(ball_vel[0] + 0.1 * ball_vel[0])
            ball_vel[1] = ball_vel[1] + 0.1 * ball_vel[1]
        else:
            score2 = score2 + 1
            spawn_ball(RIGHT)
            
    # hit the right gutter
    elif new_ball_posX >= WIDTH - PAD_WIDTH - BALL_RADIUS:
        ball_pos[0] = WIDTH - PAD_WIDTH - BALL_RADIUS
        if (ball_pos[1] >= paddle2_pos - HALF_PAD_HEIGHT 
            and ball_pos[1] <= paddle2_pos + HALF_PAD_HEIGHT):
            ball_vel[0] = -(ball_vel[0] + 0.1 * ball_vel[0])
            ball_vel[1] = ball_vel[1] + 0.1 * ball_vel[1]
        else:
            score1 = score1 + 1
            spawn_ball(LEFT)
    else:
        ball_pos[0] = new_ball_posX
        
    # draw ball
    canvas.draw_circle(ball_pos, BALL_RADIUS, 1, "White", "White")
    
    # update paddle's vertical position, keep paddle on the screen
    new_paddle1_pos = paddle1_pos + paddle1_vel
    if new_paddle1_pos <= HALF_PAD_HEIGHT:
        paddle1_pos = HALF_PAD_HEIGHT
    elif new_paddle1_pos >= HEIGHT-HALF_PAD_HEIGHT:
        paddle1_pos = HEIGHT-HALF_PAD_HEIGHT
    else:
        paddle1_pos = new_paddle1_pos
    
    new_paddle2_pos = paddle2_pos + paddle2_vel
    if new_paddle2_pos <= HALF_PAD_HEIGHT:
        paddle2_pos = HALF_PAD_HEIGHT
    elif new_paddle2_pos >= HEIGHT-HALF_PAD_HEIGHT:
        paddle2_pos = HEIGHT-HALF_PAD_HEIGHT
    else:
        paddle2_pos = new_paddle2_pos
    
    # draw paddles
    canvas.draw_polygon([[0, paddle1_pos-HALF_PAD_HEIGHT], 
            [PAD_WIDTH-1, paddle1_pos-HALF_PAD_HEIGHT],
            [PAD_WIDTH-1, paddle1_pos+HALF_PAD_HEIGHT], 
            [0, paddle1_pos+HALF_PAD_HEIGHT]], 1, "White", "White")
    canvas.draw_polygon([[WIDTH-PAD_WIDTH+1, paddle2_pos-HALF_PAD_HEIGHT], 
            [WIDTH, paddle2_pos-HALF_PAD_HEIGHT],
            [WIDTH, paddle2_pos+HALF_PAD_HEIGHT], 
            [WIDTH-PAD_WIDTH+1, paddle2_pos+HALF_PAD_HEIGHT]], 1, "White", "White")
    # draw scores
    canvas.draw_text(str(score1), (WIDTH/4, HEIGHT/5), 50, "Red")
    canvas.draw_text(str(score2), (WIDTH/4*3, HEIGHT/5), 50, "Red")
    canvas.draw_text("Author: Tiny656", (WIDTH/5*3, HEIGHT/20*18), 18, "Green")
    canvas.draw_text("Mail: 236798656@qq.com", (WIDTH/5*3, HEIGHT/20*19), 18, "Green")

    
def keydown(key):
    global paddle1_vel, paddle2_vel
    acc = 3
    if key == simplegui.KEY_MAP['w']:
        paddle1_vel -= acc
    elif key == simplegui.KEY_MAP['s']:
        paddle1_vel += acc
    if key == simplegui.KEY_MAP['up']:
        paddle2_vel -= acc
    elif key == simplegui.KEY_MAP['down']:
        paddle2_vel += acc
        
def keyup(key):
    global paddle1_vel, paddle2_vel
    acc = 3
    if key == simplegui.KEY_MAP['w']:
        paddle1_vel += acc
    elif key == simplegui.KEY_MAP['s']:
        paddle1_vel -= acc
    if key == simplegui.KEY_MAP['up']:
        paddle2_vel += acc
    elif key == simplegui.KEY_MAP['down']:
        paddle2_vel -= acc

def reset():
    new_game()

        
# create frame
frame = simplegui.create_frame("Pong", WIDTH, HEIGHT)
frame.set_draw_handler(draw)
frame.set_keydown_handler(keydown)
frame.set_keyup_handler(keyup)
frame.add_button("Reset", reset, 200)

# start frame
new_game()
frame.start()
原文地址:https://www.cnblogs.com/tiny656/p/4030263.html