你如何分别控制两个不同的对象(两个玩家乒乓球比赛)?

时间:2012-08-03 15:12:05

标签: python python-2.7 tkinter

所以我发现这个名叫法拉利的球员创造了一场伟大的乒乓球比赛,我被赋予了两个得分,但没有高分的任务。我已经尝试了很多东西,除非我制作了第二块控制动作的代码,它要么被完全忽略,要么我按下它分配的向上按钮,它将一直向上,不会再回来。

from decimal import Decimal, getcontext
getcontext().prec = 20
import time,random,os,sys
from Tkinter import *
from tkFileDialog import *
import tkFont
import os,sys

## decimal is for numbers and arithmatic that use irrational numbers
## Tkinter is the graphical interface for the program
## tkFont is the window that pops up
## os is operating system
## sys is system


## there are 2 different variables for player 1 and player 2 because one variable is a variable for a number, the other creates the rectangle(the actual paddle)
## these are just variables that represent numbers

refreshrate=100
do = 1
done=0
ballX=320
ballY=240
playerone=180
py2=180
pause=0
up=0
down=0
root = Tk()
root.title("Pong")
c = Canvas(root,width=640,height=480,bg='black')
c.pack()
xc = float(2)
yc = float(random.randint(-3,3))
while(yc==0):
    yc = float(random.randint(-3,3))
ball = c.create_oval(ballX-10,ballY-10,ballX+10,ballY+10,fill='white')
player = c.create_rectangle(610,playerone,625,playerone+120,fill='blue')
playertwo = c.create_rectangle(15,playerone,30,playerone+120,fill='blue')
c.create_rectangle(318,0,322,480,fill='white')
c.create_rectangle(318,0,322,30,fill='white')
score = 0
escore= 0
lives = 5
font = tkFont.Font(family = "Book Antiqua", size = 15, weight = "bold")

## to change the keys in which you press to control the paddles, change the letter or arrow key between the quotation marks next to str a   
## player1 controls

def up(event):
    global playerone, player, py2, playertwo, high, low
    up = 1
def down(event):
    global playerone, player, py2, playertwo, high, low
    down = 1
def escape(event):
    global do, lives
    lives = -1
    do = 0
    root.quit
    root.quit()
def onkeyrelease(event):
    global up, down, do, lives, pause
    key = event.keysym
    if (str(key)=="w"):
        up = 0
    if (str(key)=="s"):
        down = 0
    if (str(key)=="Escape" and done==1):
        root.quit
        do = 0
        lives = -1
        root.quit()
    if (str(key)=="p"):
        if(pause==1):
            pause=0
        elif(pause==0):
            pause=1
def buttoninit():
    root.bind("<w>",up)
    root.bind("<s>",down)
    root.bind("`",escape)
    root.bind('<KeyRelease>', onkeyrelease)
buttoninit()

## player2 controls

def high(event):
    global playerone, player, py2, playertwo, up, down
    up = 1
def low(event):
    global playerone, player, py2, playertwo, up, down
    down = 1
def leave(event):
    global do, lives
    lives = -1
    do = 0
    root.quit
    root.quit()
def offkeyrelease(event):
    global up, down, do, lives, pause
    key = event.keysym
    if (str(key)=="Up"):
        up = 0
    if (str(key)=="Down"):
        down = 0
    if (str(key)=="Escape" and done==1):
        root.quit
        do = 0
        lives = -1
        root.quit()
    if (str(key)=="p"):
        if(pause==1):
            pause=0
        elif(pause==0):
            pause=1
def buttononit():
    root.bind("<Up>",high)
    root.bind("<Down>",low)
    root.bind("`",leave)
    root.bind('<KeyRelease>', offkeyrelease)
buttononit()


c.create_text(475,10,text='Lives:',fill='white',font=font)
livestext = c.create_text(525,10,text=lives,fill='white',font=font)
c.create_text(475,25,text='Score:',fill='white',font=font)
scoretext = c.create_text(525,25,text=score,fill='white',font=font)
print "Push 'p' to pause"
var=1

while (do):
    ##  player movement controls
    if(playerone>0 and up==1 and down!=1 and pause==0):
        playerone=playerone-5
    if(playerone<360 and down==1 and up!=1 and pause==0):
        playerone=playerone+5

##  player collision detection
    if(ballX>=605 and ballY+10>=playerone and ballX<=616 and ballY+10>=playerone and ballY-10<=playerone+120):
        xc*=-1
        ballX=605
        if(xc>-10):
            xc=float(xc)-float(0.4)
        if(yc>0):
            yc=float(random.randint(15,70))
            yc=float(yc)/float(10)
        else: 
            yc=float(random.randint(-70,-15))
            yc=float(yc)/float(10)

##  playertwo movement controls
        if (playertwo>0 and high==1 and low!=1 and pause==0):
            playertwo=playertwo+5
        if (playertwo<360 and low==1 and high!=1 and pause==0):
            playertwo=playertwo-5

##  playertwo collision detection
    if(ballX<=40 and ballX>=29 and ballY+py2 and ballY-10<=py2+120):
        xc*=-1
        ballX=40
        if(xc<10):
            xc=float(xc)+float(0.4)
        if(yc>0):
            yc=float(random.randint(15,70))
            yc=float(yc)/float(10)
        else:
            yc=float(random.randint(-70,-15))
            yc=float(yc)/float(10)

##  left and right bounds collision detection (aka missed paddle)
    if(ballX>=630 and xc>0):
        ballX=320
        ballY=240
        xc = 2
        yc = random.randint(-3,3)
        while(yc==0):
            yc = random.randint(-3,3)
        lives = lives-1
        escore=escore+1
    if(ballX<=10 and xc<0):
        ballX=320
        ballY=240
        xc = 2
        yc = random.randint(-3,3)
        while(yc==0):
            yc = random.randint(-3,3)
        score=score+1

##  top and bottom bounds colliison detection
    if(playerone<0):
        playerone=0
    if(playerone>360):
        playerone=360
    if(ballY>=470 or ballY<=10):
        yc*=-1

##  AI(artificial intelligence(ball)) movement controls
    if(py2+60<ballY and py2<360 and xc<0 and pause==0):
        py2=py2+4
    if(py2+60>ballY and py2>0 and xc<0 and pause==0):
        py2=py2-4
    if(pause==0):
        ballX=ballX+xc
        ballY=ballY+yc
    c.delete(ball)
    ball = c.create_oval(ballX-10,ballY-10,ballX+10,ballY+10,fill='white')
    c.delete(player)
    player = c.create_rectangle(610,playerone,625,playerone+120,fill='blue')
    c.delete(playertwo)

# to change the game to single player, remove the playerone's in the following line and replace them with py2's
# this are  the coordinates for the paddle
# the first playerone, tracks the position of the thing that the paddle is following
# 1#= how wide it is, 2# tracks the other object, 3# the width, 4# how tall the paddle is, 5# the color

    playertwo = c.create_rectangle(15, playerone ,30,  playerone + 120 ,fill='blue')

    c.delete(livestext)
    livestext = c.create_text(525,10,text=lives,fill='white',font=font)
    c.delete(scoretext)
    scoretext = c.create_text(525,25,text=score,fill='white',font=font)
    c.update()
    time.sleep(Decimal("1")/Decimal("100"))
    if(lives==0):
        do=0
        if(score > 0):
            print score
        done = 1
    try:
        c.update()
    except:
        print "Error"
        root.quit
    while(lives==0):
        c.update()
try:
    c.update()
except:
    print "Error!!!"
    root.quit

1 个答案:

答案 0 :(得分:2)

您的代码已经重新设计为以下双人游戏。该程序已被重组为各种函数,类和方法。控件是相同的,没有AI可以使用。

from tkinter import *
from tkinter.font import Font
import functools
import math
import random
import time

################################################################################

class Pong(Canvas):

    DEFAULTS = dict(width=640,
                    height=480,
                    background='black',
                    highlightthickness=0)

    @classmethod
    def main(cls):
        root = Tk()
        root.title('Pong')
        root.resizable(False, False)
        root.bind_all('<Escape>', lambda event: root.destroy())
        game = cls(Font(family='Book Antiqua', size=15, weight='bold'), 5, 100,
                   background='black', width=640, height=480)
        game.grid()
        root.mainloop()

    def __init__(self, font, lives, fps, master=None, cnf={}, **kw):
        for item in self.DEFAULTS.items():
            kw.setdefault(*item)
        super().__init__(master, cnf, **kw)
        self.font = font
        self.p1 = Paddle(lives, 'blue', 10,
                         self.height, 120, 15, 5)
        self.p2 = Paddle(lives, 'blue', self.width - 10,
                         self.height, 120, 15, 5)
        self.wait = 1000 // fps
        self.separator = Box(Point(self.width // 2 - 2, 0),
                             Point(self.width // 2 + 2, self.height))
        self.new_rect(self.separator, 'white')
        self.bind('<p>', self.pause)
        self.p1.bind(self, 'w', 's')
        self.p2.bind(self, 'Up', 'Down')
        self.draw_high = True
        self.after_idle(self.startup)
        self.focus_force()

    def pause(self, event):
        if not self.running_startup:
            self.refresh = self.after_cancel(self.refresh) \
                           if self.refresh else self.after_idle(self.animate)

    def startup(self, countdown=3, target=None):
        if target is None:
            self.running_startup = True
            self.ball = Ball('white', self.width, self.height, 20)
            self.refresh = None
            target = time.clock() + countdown
        for paddle in self.p1, self.p2:
            paddle.center()
        self.draw_all()
        remaining = math.ceil(target - time.clock())
        if remaining:
            self.new_text(Point(self.width >> 1, self.height >> 1),
                          self.random_color(), str(remaining), CENTER)
            self.after(self.wait, self.startup, None, target)
        else:
            self.running_startup = False
            self.after_idle(self.animate)

    @classmethod
    def random_color(cls):
        return '#{:02X}{:02X}{:02X}'.format(*cls.random_bytes(3))

    @staticmethod
    def random_bytes(n):
        return bytes(random.randrange(1 << 8) for _ in range(n))

    def animate(self):
        self.move_all()
        if self.in_bounds():
            self.draw_all()
            self.refresh = self.after(self.wait, self.animate)

    def move_all(self):
        for obj in self.p1, self.p2, self.ball:
            obj.move()
            if obj is not self.ball:
                obj.bounce(self.ball)

    def in_bounds(self):
        if self.boundary.intersects(self.ball.boundary):
            return True
        if (self.p2 if self.ball.position.x > 0 else self.p1).kill():
            self.after_idle(self.startup)
        else:
            self.draw_all()
            self.after(5000, self.quit)
        return False

    def draw_all(self):
        self.delete('actor')
        for obj in self.p1, self.p2, self.ball:
            obj.render(self)
        self.render_status()

    def render_status(self, x_margin=4, y_margin=4):
        self.draw_high = high = (self.ball.position.y > self.height * 0.25) \
                                if self.draw_high else \
                                (self.ball.position.y >= self.height * 0.75)
        if high:
            self.new_text(self.separator.NW + Point(-x_margin, +y_margin),
                          'white', self.p1.status, NE)
            self.new_text(self.separator.NE + Point(+x_margin, +y_margin),
                          'white', self.p2.status, NW)
        else:
            self.new_text(self.separator.SW + Point(-x_margin, -y_margin),
                          'white', self.p1.status, SE)
            self.new_text(self.separator.SE + Point(+x_margin, -y_margin),
                          'white', self.p2.status, SW)

    def new_rect(self, box, color, tag='static'):
        self.create_rectangle(box, fill=color, outline=color, tag=tag)

    def new_oval(self, box, color, tag='static'):
        self.create_oval(box, fill=color, outline=color, tag=tag)

    def new_text(self, point, color, text, anchor, tag='actor'):
        self.create_text(point, fill=color, tag=tag,
                         text=text, anchor=anchor, font=self.font)

    @property
    def width(self):
        return int(self['width'])

    @property
    def height(self):
        return int(self['height'])

    @property
    def boundary(self):
        return Box(Point(0, 0), Point(self.width, self.height))

################################################################################

def enum(names):
    return type('enum', (), dict(map(reversed, enumerate(
        names.replace(',', ' ').split())), __slots__=()))()

def copy_sign(x, y):
    return type(x)(math.copysign(x, y))

################################################################################

class Paddle:

    PART = enum('null, upper, center, lower')

    def __init__(self, lives, color, alignment, board_height,
                 paddle_height, paddle_width, move_by):
        self.lives = lives
        self.color = color
        self.height = board_height
        self.position = Point(alignment, board_height >> 1)
        self.size = Point(paddle_width >> 1, paddle_height >> 1)
        self.move_by = move_by
        self.score = 0
        self.just_bounced = False

    def kill(self):
        self.lives -= 1
        self.score >>= 1
        return self.lives > 0

    def center(self):
        y, middle = self.position.y, self.height >> 1
        if y < middle:
            self.move(down=True)
        elif y > middle:
            self.move(up=True)

    def move(self, *, up=False, down=False):
        if up or (not down and self.keys.up and
                  self.position.y - self.size.y > 0):
            self.position -= Point(0, self.move_by)
        if down or (not up and self.keys.down and
                    self.position.y + self.size.y < self.height):
            self.position += Point(0, self.move_by)

    def bounce(self, ball):
        minimum = self.size.x + ball.radius
        if self.position.x != ball.position.x and self.overlap(ball, minimum):
            if not self.just_bounced:
                self.just_bounced = True
                self.score += abs(ball.velocity.y)
            sign = +1 if self.position.x < ball.position.x else -1
            if self.collision_area == self.PART.center:
                ball.position.x = self.position.x + minimum * sign
            else:
                ball.position.adjust(self.middle_point, minimum)
            ball.velocity.x = copy_sign(ball.velocity.x, sign)
            ball.change_speed()
        else:
            self.just_bounced = False

    def overlap(self, ball, minimum):
        box = self.boundary
        if box.intersects(ball.boundary):
            self.collision_area = self.PART.center
        elif (self.hi_mid(box) - ball.position).magnitude <= minimum:
            self.collision_area = self.PART.upper
        elif (self.lo_mid(box) - ball.position).magnitude <= minimum:
            self.collision_area = self.PART.lower
        else:
            self.collision_area = self.PART.null
        return self.collision_area

    def render(self, surface):
        box = self.boundary
        surface.new_rect(box, self.color, 'actor')
        surface.new_oval(Box.from_point(self.hi_mid(box), self.size.x),
                         self.color, 'actor')
        surface.new_oval(Box.from_point(self.lo_mid(box), self.size.x),
                         self.color, 'actor')

    def hi_mid(self, boundary):
        self.middle_point = Point(self.position.x, boundary.a.y)
        return self.middle_point

    def lo_mid(self, boundary):
        self.middle_point = Point(self.position.x, boundary.b.y)
        return self.middle_point

    def bind(self, surface, up, down):
        self.keys = KeyListener(surface, up=up, down=down)

    @property
    def boundary(self):
        return Box.from_point(self.position, self.size)

    @property
    def status(self):
        return 'Lives: {}\nScore: {}'.format(self.lives, self.score)

Player = Paddle

################################################################################

class KeyListener:

    def __init__(self, widget, **kwargs):
        self.__state = dict.fromkeys(kwargs, False)
        for name, key in kwargs.items():
            widget.bind('<KeyPress-{}>'.format(key), self.__set(name, True))
            widget.bind('<KeyRelease-{}>'.format(key), self.__set(name, False))

    def __set(self, name, value):
        def handler(event):
            self.__state[name] = value
        return handler

    def __getattr__(self, name):
        return self.__state[name]

################################################################################

class Ball:

    def __init__(self, color, width, height, size):
        self.color = color
        self.board = Point(width, height)
        self.position = self.board / 2
        self.radius = size >> 1
        self.velocity = Point(1 - 2 * random.randrange(2),
                              1 - 2 * random.randrange(2))
        self.change_speed()

    def change_speed(self, max_x=10, max_y=10):
        speed = self.velocity
        speed.x = copy_sign(random.randint(1, max_x), speed.x)
        speed.y = copy_sign(random.randint(1, max_y), speed.y)

    def move(self):
        self.position += self.velocity
        self.bounce()

    def bounce(self):
        if self.position.y - self.radius < 0:
            self.position.y = self.radius
            self.velocity.y = copy_sign(self.velocity.y, +1)
            self.change_speed()
        elif self.position.y + self.radius > self.board.y:
            self.position.y = self.board.y - self.radius
            self.velocity.y = copy_sign(self.velocity.y, -1)
            self.change_speed()

    def render(self, surface):
        surface.new_oval(self.boundary, self.color, 'actor')

    @property
    def boundary(self):
        return Box.from_point(self.position, self.radius)

################################################################################

def autocast(function):
    @functools.wraps(function)
    def cast(self, other):
        if not isinstance(other, self.__class__):
            other = self.__class__(other, other)
        return function(self, other)
    return cast

################################################################################

class Point(list):

    def __init__(self, x, y):
        super().__init__((x, y))

    def __repr__(self):
        return '{}({})'.format(self.__class__.__name__,
                               ', '.join(map(repr, self)))

    @autocast
    def __add__(self, other):
        return self.__class__(self.x + other.x, self.y + other.y)

    @autocast
    def __sub__(self, other):
        return self.__class__(self.x - other.x, self.y - other.y)

    @autocast
    def __mul__(self, other):
        return self.__class__(self.x * other.x, self.y * other.y)

    @autocast
    def __truediv__(self, other):
        return self.__class__(self.x / other.x, self.y / other.y)

    @autocast
    def __floordiv__(self, other):
        return self.__class__(self.x // other.x, self.y // other.y)

    @autocast
    def __iadd__(self, other):
        self.x += other.x
        self.y += other.y
        return self

    @autocast
    def __isub__(self, other):
        self.x -= other.x
        self.y -= other.y
        return self

    def __get_x(self):
        return self[0]

    def __set_x(self, value):
        self[0] = value

    x = property(__get_x, __set_x)

    def __get_y(self):
        return self[1]

    def __set_y(self, value):
        self[1] = value

    y = property(__get_y, __set_y)

    def __get_magnitude(self):
        return math.hypot(self.x, self.y)

    def __set_magnitude(self, value):
        magnitude = self.magnitude
        self.x *= value / magnitude
        self.y *= value / magnitude

    magnitude = property(__get_magnitude, __set_magnitude)

    def adjust(self, projected_from, distance):
        vector = self - projected_from
        vector.magnitude = distance
        self.x = round(projected_from.x + vector.x)
        self.y = round(projected_from.y + vector.y)

################################################################################

class Box(list):

    @classmethod
    def from_point(cls, point, extension):
        return cls(point - extension, point + extension)

    def __init__(self, a, b):
        super().__init__((a, b))

    def __repr__(self):
        return '{}({})'.format(self.__class__.__name__,
                               ', '.join(map(repr, self)))

    def intersects(self, other):
        return not (self.a.x > other.b.x or other.a.x > self.b.x or
                    self.a.y > other.b.y or other.a.y > self.b.y)

    def __get_a(self):
        return self[0]

    def __set_a(self, value):
        self[0] = value

    a = NW = property(__get_a, __set_a)

    def __get_b(self):
        return self[1]

    def __set_b(self, value):
        self[1] = value

    b = SE = property(__get_b, __set_b)

    @property
    def NE(self):
        return Point(self.b.x, self.a.y)

    @property
    def SW(self):
        return Point(self.a.x, self.b.y)

################################################################################

if __name__ == '__main__':
    Pong.main()

编辑:在设计评分系统时,得分和生命没有意义。如果当对手失去时球员的得分增加1,则得分可以被丢弃,并且获胜者将是没有失去所有生命的人。如果每次击球时球员的得分增加1,则两个球员在整个比赛中的得分几乎相同,并且得分相当无意义。

我选择根据近似击球的难度来增加球员的得分。如果球碰巧有五个垂直移动并且球员击球,则球员的得分增加五。为了进一步激励不要放松生命,玩家的分数在失去生命后减半。这也为其他球员提供了充分的机会来赶上得分。

对于奇怪的弹跳,每次撞击物体时球的速度都会随机变化。这样做的原因是,如果球保持相同的速度并且正常弹跳,那么很容易预测球将要去哪里,而且比赛太简单了。如果玩家在游戏中没有发现任何挑战,那么无聊和无敌会导致游戏迅速放弃。

当球足够接近时,状态显示移动到棋盘的另一侧,以便状态和球永远不会相互叠加,从而使潜在的球员感到困惑。球的移动超过25%的屏幕高度或球向下移动超过75%的屏幕高度,从而触发更改。如果您认为游戏中存在任何不同之处,请考虑学习Python,以便您自己修改游戏。