本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章。
1.前言
游戏,大家一定不陌生,那么有没有想过游戏是怎么做出来的呢?作为一个与代码打交道的人,都知道是用一行一行代码堆积出来的。今天,大家就跟小编一起来用代码敲出一款属于自己的游戏吧!
2.准备
今天要做的是一款类似于打乒乓球的小游戏,要做这款游戏必须要有一定python基础,当然只要用心,小白也是可以学会的。
小编将通过以下几个步骤来做这款游戏:
(1)配置环境
(2)设置背景
(3)绘制玩家
(4)方向控制
(5)添加乒乓球
(6)边界判断以及接球检测
(7)分数设置
3.游戏制作
1.配置环境。
首先安装python环境。详情见python官网。
(https://www.python.org/)
这款游戏用到的只有一个工具就是python的“小海龟”——turtle,这是python的内置库。只需要调用就行。这里将turtle调用为t,方便后续使用。
import turtle as t
2.设置背景。
设置背景之前,先想一想背景需要有什么特征。自然是要有宽和高、还有标题、颜色。这里就需要用到turtle的函数,具体看代码。
game = t.Screen() #绘制游戏框
game.title('八里公路打乒乓')
game.bgcolor('black')
game.setup(800,600) #宽和高
3.绘制玩家。
同样,绘制玩家时,也需要知道玩家的特征。就是大小、颜色、形状、还有位置。这里需要注意的是turtle的坐标系是怎样的。它同pygame(python的游戏库)不一样的是,它的画布的正中央为坐标原点,所以玩家的位置就要根据玩家大小和游戏窗口大小来计算得出。还有由于玩家是可以移动的,所以其坐标就不能写死,就用两个变量x,y来表示。详见代码。
p1 = t.Turtle()
p1.ht() #隐藏画笔的标识
p1.up() #提起画笔,防止移动时留下痕迹
p1.color('yellow')
p1.speed(5) #移动速度
p1.shape('square') #玩家形状
p1.shapesize(5,1) #大小长宽为5:1
p1.goto(-350,0)
p1.st()
p1_score = 0 #玩家1的得分,后续会用
def p1_up(): #移动的方法,后面可以直接调用
y = p1.ycor()
y += 20
p1.sety(y)
def p1_down():
y = p1.ycor()
y -= 20
p1.sety(y)
同理,再绘制出玩家2.这里先看一下效果;
图1玩家绘制效果
4.方向控制。
要控制方向,就需要程序知道键盘动作,所以turtle中有一个listen()函数,可以知道键盘上的动作,具体的哪个按键控制什么动作,都是可以自己设置的。
game.listen()
game.onkey(p1_up,'s') #移动方向对应按键的设置
game.onkey(p1_down,'x') #对应的移动在绘制玩家代码中,调用移动方法。
game.onkey(p2_up,'Up')
game.onkey(p2_down,'Down')
注意:在游戏时,只有英文输入法键盘才能控制。
5.添加乒乓球。
添加方法和添加玩家是一样的,不同的是其形状为圆形,位置也要注意,既然是双人游戏,为了公平起见,选择在窗口中心坐标原点出发球。一样要注意坐标不能写死了,也用两个变量表示。还有乒乓球是自己动的,所以还需要给它一个移动“规则”,也就是dx,和dy,这样就是沿着一条斜线移动的。
pp = t.Turtle()
pp.up()
pp.color('white')
pp.speed(0)
pp.shape('circle')
pp.st()
pp.dx = 3
pp.dy = 3
pp.setx(pp.xcor()+pp.dx) #位置移动
pp.sety(pp.ycor()+pp.dy)
6.边界判断以及接球检测。上述步骤都是比较容易的,没有什么逻辑关系,关键在于对turtle的用法掌握。而这一步骤就需要比较强的编程思维以及一定数学能力了。由于玩家是可以自己移动的,所以就不用管玩家的界限。对于乒乓球而言,它首先是从原点往右上方移动,最先会撞到上方的边界,根据物理的光学反射原理,可以得出球撞到上边界时会以它的入射角度反方向移动,放到坐标系上来看,就是横坐标不变,被反弹回来后其纵坐标变为相反数,所以只需要将乒乓球的移动规则的dy乘上-1就可以了。同理,下边界也是一样的道理。至于坐标的范围就需要自己在纸上画一画了。
pp.setx(pp.xcor()+pp.dx) #位置移动
pp.sety(pp.ycor()+pp.dy)
if pp.ycor() > 290 or pp.ycor() < -290: #上下边界范围
pp.dy *= -1
判断了上下边界,然后再判断左右边界,跟上下边界判断方法一样,不同的是球左右出界后不会反弹。而是回到原点重新开始。
if pp.xcor()>380: #右边界范围
pp.goto(0,0)
p1_score += 1 #球在哪一边出界了,它的对手就加以分。
score()
if pp.xcor()<-380: #左边界范围
pp.goto(0,0)
p2_score += 1
score()
再来看接球检测,如果球的坐标与球拍的坐标距离在一定的范围内了,就要让球反弹,dy不变,dx乘上-1。为了防止球在球拍上的粘连情况,让球反弹时适当往球拍外移动一点,这里选择将球的横坐标改变为339或-339.
if pp.ycor()<p2.ycor()+50 and pp.ycor()>p2.ycor()-50 and pp.xcor()>340:
pp.dx *= -1
pp.setx(339)
if pp.ycor()<p1.ycor()+50 and pp.ycor()>p1.ycor()-50 and pp.xcor()<-340:
pp.dx *= -1
pp.setx(-339)
7.分数设置。可以直接用turtle的write()函数,然后再自己设置字的颜色,大小,位置。
def score():
pen.clear() #防止叠加
pen.write('八里公路:%d VS 大魔王:%d' % (p1_score,p2_score),align='center',font=('Arial',20,'bold'))
score()
特别注意的是,由于turtle的工作原理是一遍一遍的画,所以需要定义一个主循环,让需要的一直出现在窗口中。还要防止文字叠加,所以每一画的时候,都要清空画笔,然后update更新。
#判断是否退出
running = True
def stop_loop():
global running
running = False
#注册退出事件
root = game.getcanvas().winfo_toplevel()
root.protocol('WM_DELETE_WINDOW',stop_loop)
while running:
game.update()
上面的几个看不懂的东西,只需要记住就行了,它的目的是让电脑知道叉掉窗口是要退出游戏的意思,这样才退出时不会报错。
4.综合代码
经过以上所有步骤,这个游戏就算是做好了,来看一下完整代码吧。
import turtle as t
#设置背景
game = t.Screen() #绘制游戏框
game.title('八里公路打乒乓')
game.bgcolor('black')
game.setup(800,600) #宽和高
game.tracer()
#玩家1
p1 = t.Turtle()
p1.ht() #隐藏画笔的标识
p1.up() #提起画笔,防止移动时留下痕迹
p1.color('yellow')
p1.speed(5) #移动速度
p1.shape('square') #玩家形状
p1.shapesize(5,1) #大小长宽为5:1
p1.goto(-350,0)
p1.st()
p1_score = 0 #玩家1的得分,后续会用
def p1_up(): #玩家1的坐标,移动时使用,这里是向上
y = p1.ycor()
y += 20 #移动步长
p1.sety(y)
def p1_down(): #对应的移动在绘制玩家代码中,调用移动方法。
y = p1.ycor()
y -= 20
p1.sety(y)
#玩家2
p2 = t.Turtle()
p2.ht()
p2.up()
p2.color('red')
p2.speed(5)
p2.shape('square')
p2.shapesize(5,1)
p2.goto(350,0)
p2.st()
p2_score = 0
def p2_up():
y = p2.ycor()
y += 20
p2.sety(y)
def p2_down():
y = p2.ycor()
y -= 20
p2.sety(y)
#添加乒乓
pp = t.Turtle()
pp.up()
pp.color('white')
pp.speed(0)
pp.shape('circle')
pp.st()
pp.dx = 3
pp.dy = 3
#添加分数
pen = t.Turtle()
pen.ht()
pen.color('white')
pen.up()
pen.goto(0,250)
pen.down()
def score():
pen.clear() #防止叠加
pen.write('八里公路:%d VS 大魔王:%d' % (p1_score,p2_score),align='center',font=('Arial',20,'bold'))
score()
pen1 = t.Turtle()
pen1.ht()
pen1.up()
pen1.color('white')
pen1.goto(0,220)
pen1.write('左:s(上),x(下);右:方向键控制上下',align='center',font=('Arial',15,'bold'))
#玩家移动
game.listen()
game.onkey(p1_up,'s') #移动方向对应按键的设置
game.onkey(p1_down,'x') #对应的移动在绘制玩家代码中,调用移动方法。
game.onkey(p2_up,'Up')
game.onkey(p2_down,'Down')
#判断是否退出
running = True
def stop_loop():
global running
running = False
#注册退出事件
root = game.getcanvas().winfo_toplevel()
root.protocol('WM_DELETE_WINDOW',stop_loop)
#乒乓移动
while running:
game.update()
pp.setx(pp.xcor()+pp.dx) #位置移动
pp.sety(pp.ycor()+pp.dy)
if pp.ycor() > 290 or pp.ycor() < -290:
pp.dy *= -1
#接球
if pp.ycor()<p2.ycor()+50 and pp.ycor()>p2.ycor()-50 and pp.xcor()>340:
pp.dx *= -1
pp.setx(339)
if pp.ycor()<p1.ycor()+50 and pp.ycor()>p1.ycor()-50 and pp.xcor()<-340:
pp.dx *= -1
pp.setx(-339)
#球出界
if pp.xcor()>380: #右边界范围
pp.goto(0,0)
p1_score += 1
score()
if pp.xcor()<-380: #左边界范围
pp.goto(0,0)
p2_score += 1
score()
然后再看以下运行效果。
图2 最终效果
5.总结
做完这个简单的游戏后,不知道大家对写代码有了什么新的看法?
当然做游戏不是主要目的,只是通过做游戏来感受以下编程带来的乐趣,毕竟可视化后是要比代码有趣多了。而且,大家也看到了,仅仅是这样一个简单的游戏,也是需要不少功夫的,所以,在编游戏的同时也能提高编程思维,以及解决问题的方法步骤。那么,你是不是也想跟小编一起做做小游戏呢?
END
主 编 | 王文星
责 编 | 江来洪
where2go 团队