zoukankan      html  css  js  c++  java
  • 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()
  • 相关阅读:
    Java堆、栈和常量池
    Java多线程内存模型
    To-do List
    Java Collections Framework 汇总
    关于ArrayList.clear()与=null以及new ArrayList<E>()
    开源协议
    git-svn — 让git和svn协同工作
    Java Collections Framework 之 RandomAccess接口
    转 : CSS Modules详解及React中实践
    转 : JBoss Web和 Tomcat的区别
  • 原文地址:https://www.cnblogs.com/tiny656/p/4030263.html
Copyright © 2011-2022 走看看