你如何分别控制两个不同的对象(两人乒乓球游戏)? [英] How do you control two different objects separately(two player pong game)?
问题描述
所以我发现了这个叫 Ferrari 的家伙创造的这款很棒的乒乓球游戏,我的任务是让两个人玩两个分数但没有高分.我已经尝试了很多东西,除了当我制作第二个控制运动的代码块时,它要么被完全忽略,要么我会按下它分配的向上按钮,它会一直向上移动并且不会返回.
So I found this great game of pong created by this guy called Ferrari, and I was given the task to make it two playered with two scores but with no high score. I've tried many things except when I make a second bock of code that controls the movements it either gets completely ignored or I will press the up button that its assigned and it will go all the way up and won't come back down.
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
推荐答案
您的代码已重新设计为以下两人游戏.该程序已被重组为各种函数、类和方法.没有可玩的 AI,控件是相同的.
Your code has been reworked into the following two-player game. The program has been reorganized into a variety of functions, classes, and methods. The controls are the same without an AI to play with.
from tkinter import *
from tkinter.font import Font
import functools
import math
import random
import time; time.clock = time.perf_counter
################################################################################
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,那么整个比赛双方球员的得分几乎相同,而这些得分毫无意义.
In designing the scoring system, it did not make sense to have a score and lives. If a player's score is incremented by one when the opponent looses, scores could be dropped, and the winner would be the person who did not loose all lives. If a player's score is incremented by one each time the ball is hit, both players will have nearly identical scores throughout the game, and the scores are fairly meaningless.
我选择根据击球难度的近似值来增加球员的得分.如果球碰巧垂直移动了 5 并且球员击球,那么球员的分数就会增加 5.为了进一步激励不要失去生命,玩家的分数在失去生命时减半.这也为其他球员追赶得分提供了充足的机会.
I chose to increment a player's score based on an approximation of how difficult the ball was to hit. If the ball happened to have a vertical movement of five and the player hits the ball, then the player's score is incremented by five. To add further incentive not to loose lives, a player's score is halved upon loosing a life. That also provides ample opportunity for the other player to catch up in scoring.
至于奇怪的弹跳,球每次撞击物体都会随机改变速度.这样做的原因是,如果球保持相同的速度并正常反弹,那么很容易预测球要去哪里,并且游戏会太容易了.如果玩家在游戏中没有发现挑战,无聊和无敌会导致游戏迅速放弃.
As for the weird bouncing, the ball will have a random change in speed each time it hits an object. The reason for this is that if the ball stayed at the same speed and bounced normally, it would be very easy to predict where the ball was going to go, and the game would be too easy. If the player finds no challenge in the game, being bored and invincible would lead to rapid abandonment of the game.
当球足够接近时,状态显示会移动到棋盘的另一侧,这样状态和球就不会最终彼此重叠,从而使潜在玩家感到困惑.该变化由球向上移动超过 25% 的屏幕高度或向下移动超过 75% 的屏幕高度触发.如果您觉得游戏有什么不同之处,请考虑学习 Python,以便您可以自己修改游戏.
The status display moves to the opposite side of the board when the ball gets close enough so that the status and ball never end up on top of each other, confusing potential players. The change is triggered by the ball's moving up past 25% screen height or the ball's moving down past 75% screen height. If there is anything that you feel should be different about the game, please consider learning Python so that you can modify the game yourself.
这篇关于你如何分别控制两个不同的对象(两人乒乓球游戏)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!