Python实战案例:Python使用cocos2d实现捕鱼达人

Python实战案例:Python使用cocos2d实现捕鱼达人

游戏,它是一种基于物质需求满足之上的,在一些特定时间、空间范围内遵循某种特定规则的,追求精神世界需求满足的社会行为方式。合理适度的游戏允许人类在模拟环境下挑战和克服障碍,可以帮助人类开发智力、锻炼思维和反应能力。一般的小型游戏都是一些随机变化的事物,根据鼠标或键盘的操作来完成对变化事物的点击操作或者控制操作,最终实现娱乐的目的。

如下图为地铁跑酷的界面。

上图中人就是一个控制的元素,通过控制人的左右和跳跃,最终对金币进行累加的游戏。

一、游戏框架cocos2d的介绍

Cocos2D 是一个基于MIT协议的开源框架,用于构建游戏、应用程序和其他图形界面交互应用。可以让你在创建自己的多平台游戏时节省很多的时间。

Cocos2d有几个版本,python也可以安装cocos2d框架以实现游戏的开发。安装模块命令如下:

pip3 install cocos2d

在cocos2dx的开发中,导演,场景,层是必须要使用的基本元素。

在一个游戏中,导演只有一个,在程序启动的时候,会初始化一个导演,然后再由导演调用一个场景作为第一个显示的场景开启应用程序。如下图所示导演就调用了cocos2d的图标文件做为场景展示开启应用程序。

在cocos2d的每一个场景中又可以包含有多个层。

每一个层中可以包含多个精灵,控件等基本的元素,而用户看到的就是这些基本的元素。

二、Python调用cocos2d导演显示捕鱼达人背景图片的场景

现在,就要进行捕鱼达人游戏的开发,首先要有背景图片,这里为捕鱼达人的背景图片实现一个类,这个类继承于cocos2d场景的层,在初始化函数中启用导演director获取屏幕窗口的大小,实例化cocos2d中的精灵角色,并传入背景图片做为精灵,设置精灵在屏幕窗口中显的位置,背景图片一般全窗口显示,设置position中心位置为宽和高的一半,将这个层中添加这个精灵。代码如下。

class Background(cocos.layer.Layer):
    def __init__(self):
        super(Background,self).__init__()
        self.width,self.height=cocos.director.director.get_window_size()
        sprite=cocos.sprite.Sprite("textures/bg.jpg")
        sprite.position=self.width//2,self.height//2
        self.add(sprite)

把背景图片类定义成功后,在主程序中初始化导演,初始化时传入参数宽度800,高度400。接着实例化背景图类,在cocos2d的场景Scene类中传入实例化后的背景图类,最后调用cocos2d中导演的run方法运行传入参数后的实例化场景,代码如下。

import cocos
class Background(cocos.layer.Layer):
    def __init__(self):
        super(Background,self).__init__()
        self.width,self.height=cocos.director.director.get_window_size()
        sprite=cocos.sprite.Sprite("textures/bg.jpg")
        sprite.position=self.width//2,self.height//2
        self.add(sprite)
if __name__=="__main__":
    cocos.director.director.init(width=800,height=480);
    background=Background();
    main_scene=cocos.scene.Scene(background)
    cocos.director.director.run(main_scene)

运行上述程序后,显示最终捕鱼达人的背景图展示。

三、在捕鱼达人的场景中添加游动的鱼类的精灵

游戏的场景准备完毕,现在需要在捕鱼达人的场景中添加游动的鱼类,游动的鱼类图片在命名上有一些特点,每个鱼对应了10个状态的游动姿势。如下图所示的命令和简略图显示。

由图片中的内容可知,文件名中fish是所有鱼类的统一命名,紧跟其后的04就是第四种鱼类,后面再跟一条“下划线”,然后跟上“01”-“10”的数字,分别代表第四种鱼类游动过程中的状态,连续这10张图片就是第四种鱼类的游动效果。

需要说明的是,cocos2d使用pyglet来加载图片资源。pyglet模块的安装如下:

pip3 install pyglet

pyglet中resource模块可以加载需要每帧每帧进行显示的图片资源,将这些图片资源做为pyglet中resource模块的image类加入资源列表中,将此列表做为pyglet中图片image模块的Animation类的方法fromimagesequence实现从一个序列加载动画类的图片,在构建这个动画类图片之前也要构造出图片序列的图片名,将这个动画图片实现的逻辑方法封装成鱼类,这样在背景类的初始化程序中初始化鱼类,再将这个初始化的鱼类精灵添加到场景中。代码如下。

import cocos
import pyglet
class Fish(cocos.sprite.Sprite):
    def __init__(self):
        textures=[]
        for i in range(1,11):
            name_i="0"+str(i) if i<10 else str(i)
            fish_name_i="textures/fish04_"+name_i+".png"
            texture=pyglet.resource.image(fish_name_i)
            textures.append(texture)
        animation=pyglet.image.Animation.from_image_sequence(textures,0.1)
        super(Fish, self).__init__(animation)
        self.position = 500,200
class Background(cocos.layer.Layer):
    def __init__(self):
        super(Background,self).__init__()
        self.width,self.height=cocos.director.director.get_window_size()
        sprite=cocos.sprite.Sprite("textures/bg.jpg")
        sprite.position=self.width//2,self.height//2
        self.add(sprite)
        fish=Fish()
        self.add(fish)
if __name__=="__main__":
    cocos.director.director.init(width=800,height=480);
    background=Background();
    main_scene=cocos.scene.Scene(background)
    cocos.director.director.run(main_scene)

代码的运行结果如下图所示。

由图上可知,有一条鱼在屏幕中间摆动。现在就需要这条鱼能够进行游动,也就是从屏幕中的某一个点到另一个点,cocos2d中的actions模块中涉及到精灵的位置方面的运动,MoveTo完成从某一点运动到某一个点,具体格式如下。

cocos.actions.MoveTo((x轴坐标点,y辆坐标点),duration=运动的时间周期)

上面语句的运动过程需要精灵执行do语句,这样精灵就会把移动操作的方法作用到自己的身上。具体语句格式如下。

精灵.do(cocos.actions.MoveTo((x轴坐标点,y辆坐标点),duration=运动的时间周期))

根据这样的语句格式,现在将这条鱼精灵从屏幕左端移动到屏幕右端,就是初始化的时候把这条鱼精灵放在屏幕左端,然后在初始化的时候调用鱼游动的方法swim,在swim方法体中使用do语句与MoveTo语句共同作用的结果,实现鱼精灵在屏幕中从左到右的游动效果。代码如下。

import cocos
import pyglet
class Fish(cocos.sprite.Sprite):
    def __init__(self):
        textures=[]
        for i in range(1,11):
            name_i="0"+str(i) if i<10 else str(i)
            fish_name_i="textures/fish04_"+name_i+".png"
            texture=pyglet.resource.image(fish_name_i)
            textures.append(texture)
        animation=pyglet.image.Animation.from_image_sequence(textures,0.1)
        super(Fish, self).__init__(animation)
        self.position = 800,200
        self.swim()
    def swim(self):
        self.do(cocos.actions.MoveTo((-20,200),8))

class Background(cocos.layer.Layer):
    def __init__(self):
        super(Background,self).__init__()
        self.width,self.height=cocos.director.director.get_window_size()
        sprite=cocos.sprite.Sprite("textures/bg.jpg")
        sprite.position=self.width//2,self.height//2
        self.add(sprite)
        fish=Fish()
        self.add(fish)
if __name__=="__main__":
    cocos.director.director.init(width=800,height=480);
    background=Background();
    main_scene=cocos.scene.Scene(background)
    cocos.director.director.run(main_scene)

代码中初始化时position的位置参数是(800,200),其中800是width的值,480是height的值,这两个值也是主程序中director导演初始化时决定的。代码又定义一个swim方法,在swim方法中利用do方法执行该精灵的MoveTo位置改变方法,同时在精灵类初始化方法init中也调用swim方法。

代码运行的结果如下图所示。

由运行结果图可知,一条鱼精灵会把游动的动作连贯起来,并从屏幕窗口的右边游动到屏幕窗口的左边。但是,当游动一程后,就不再游动了,往往在游戏中,我们是希望游出界的鱼类还能在另一端随机出现,这样,鱼类精灵会不停地在游戏窗口中产生,把平静的池塘背景图变得热闹起来,鱼类精灵也把捕鱼达人的界面运动了起来。那么,就在于如何将这条游出窗口界面的鱼再次显示在屏幕上,也就是不停地调用鱼游动的方法,需要在do和MoveTo共同作用的语句上加上参数+ cocos.actions.CallFunc(self.swim),这是相当于MoveTo在参数上使用了回调函数,这个回调函数的指向还是当前类中鱼精灵游动的方法。代码如下。

import cocos
import pyglet
class Fish(cocos.sprite.Sprite):
    def __init__(self):
        textures=[]
        for i in range(1,11):
            name_i="0"+str(i) if i<10 else str(i)
            fish_name_i="textures/fish04_"+name_i+".png"
            texture=pyglet.resource.image(fish_name_i)
            textures.append(texture)
        animation=pyglet.image.Animation.from_image_sequence(textures,0.1)
        super(Fish, self).__init__(animation)
        self.position = 800,200
        self.swim()
    def swim(self):
        self.position=800,200
        self.do(cocos.actions.MoveTo((-20,200),8)+ cocos.actions.CallFunc(self.swim))

class Background(cocos.layer.Layer):
    def __init__(self):
        super(Background,self).__init__()
        self.width,self.height=cocos.director.director.get_window_size()
        sprite=cocos.sprite.Sprite("textures/bg.jpg")
        sprite.position=self.width//2,self.height//2
        self.add(sprite)
        fish=Fish()
        self.add(fish)
if __name__=="__main__":
    cocos.director.director.init(width=800,height=480);
    background=Background();
    main_scene=cocos.scene.Scene(background)
    cocos.director.director.run(main_scene)

代码中在鱼类精灵的swim方法中增加了一条把精灵的位置position重新调整到800,200这个位置,然后在MoveTo的action动作方法中后面再用加号加上一个callfunc回调函数,在回调方法中继续使用swim方法。

运行后,该条鱼精灵就可以一直从屏幕右端移动到屏幕左端。运行结果如下图所示。

四、游动的鱼类精灵接收鼠标事件

现在已经完成在一个捕鱼达人的池塘中有一条游动的鱼精灵,这里实现简单的捕鱼达人方法,点击这条鱼的时候,鱼就会从屏幕中消失,鱼类精灵消失的方法可以调用cocos2中对actions动作的停止方法stop(),然后调用kill()方法删除该鱼类精灵,封装在Fish中的explode方法,具体代码如下。

import cocos
import pyglet
class Fish(cocos.sprite.Sprite):
    def __init__(self):
        textures=[]
        for i in range(1,11):
            name_i="0"+str(i) if i<10 else str(i)
            fish_name_i="textures/fish04_"+name_i+".png"
            texture=pyglet.resource.image(fish_name_i)
            textures.append(texture)
        animation=pyglet.image.Animation.from_image_sequence(textures,0.1)
        super(Fish, self).__init__(animation)
        self.position = 800,200
        self.swim()
    def swim(self):
        self.position=800,200
        self.do(cocos.actions.MoveTo((-20,200),8)+ cocos.actions.CallFunc(self.swim))
    def explode(self):
        self.stop()
        self.kill()

class Background(cocos.layer.Layer):
    def __init__(self):
        super(Background,self).__init__()
        self.width,self.height=cocos.director.director.get_window_size()
        sprite=cocos.sprite.Sprite("textures/bg.jpg")
        sprite.position=self.width//2,self.height//2
        self.add(sprite)
        fish=Fish()
        self.add(fish)
if __name__=="__main__":
    cocos.director.director.init(width=800,height=480);
    background=Background();
    main_scene=cocos.scene.Scene(background)
    cocos.director.director.run(main_scene)

这段代码中在Fish类中增加方法explode,方法体中调用stop停止动画的方法和kill删除精灵的方法。但是,目前来讲,这个explode 方法并没有被调用。所以这个鱼类精灵目前还没有办法从屏幕上消除掉。

如果要实现消除掉的效果,就需要增加一些鼠标事件,也就是需要在cocos2d的导演窗口中增加一些处理事件,window类有一个称为推处理程序的方法,它使我可以将方法推入事件堆栈。语句如下。

cocos.director.director.window.push_handlers(self.on_mouse_press)

上述代码语句中的pushhandlers就是为windows添加了一些处理,叫推处理程序,onmousepress方法是自己定义的鼠标点击方法,这个方法使用了cocos2d导演的窗口方法pushhandlers作用在了程序窗口中。此语句是定义在该对象类的onenter方法中,也就是进入到该对象的时候就会触发这样的方法。完整的onenter方法中的语句代码如下。

def on_enter(self):
    super(Fish, self).on_enter()
    cocos.director.director.window.push_handlers(self.on_mouse_press)

这里有一个回调方法onmousepress,在Fish鱼对象的精灵类中添加一个方法,其名字为onmousepress,方法体中的逻辑目前可以直接调用explode方法,也就是点击屏幕中的任何地方,都会触发鱼精灵对象的消失。代码如下。

import cocos
import pyglet
class Fish(cocos.sprite.Sprite):
    def __init__(self):
        textures=[]
        for i in range(1,11):
            name_i="0"+str(i) if i<10 else str(i)
            fish_name_i="textures/fish04_"+name_i+".png"
            texture=pyglet.resource.image(fish_name_i)
            textures.append(texture)
        animation=pyglet.image.Animation.from_image_sequence(textures,0.1)
        super(Fish, self).__init__(animation)
        self.position = 800,200
        self.swim()
    def swim(self):
        self.position=800,200
        self.do(cocos.actions.MoveTo((-20,200),8)+ cocos.actions.CallFunc(self.swim))
    def on_enter(self):
        super(Fish, self).on_enter()
        cocos.director.director.window.push_handlers(self.on_mouse_press)
    def on_mouse_press(self,x,y,button,modifier):
        self.explode()
    def explode(self):
        self.stop()
        self.kill()

class Background(cocos.layer.Layer):
    def __init__(self):
        super(Background,self).__init__()
        self.width,self.height=cocos.director.director.get_window_size()
        sprite=cocos.sprite.Sprite("textures/bg.jpg")
        sprite.position=self.width//2,self.height//2
        self.add(sprite)
        fish=Fish()
        self.add(fish)
if __name__=="__main__":
    cocos.director.director.init(width=800,height=480);
    background=Background();
    main_scene=cocos.scene.Scene(background)
    cocos.director.director.run(main_scene)

代码中Fish鱼类精灵的onmousepress方法是需要4个参数的,不然运行的时候会报错。这4个参数的意义,x和y代表了鼠标点击的位置,button代表了鼠标的按键,可以通过这个参数来判断是按动了鼠标的左键还是鼠标的右键,最后的参数modifier用于描述组合键。现在运行上面的程序后。首先有一条鱼精灵从屏幕右端游动到屏幕的左端。如下图所示。

点击屏幕中任何一处的位置,都可以消除该鱼类精灵,如下图所示。

如果限制用户的坐标点只能在鱼精灵的身上才能消除该精灵,就需要通过坐标点来控制,可以可以限制当用户点击的横坐标点大于鱼的左边,小于鱼的左边坐标加上鱼的宽度,再检测纵坐标点大于鱼的上边,小于鱼的上边加上鱼的宽度,这样的判断条件可以用下图来表示:

上图对定的条件判断式为:

if x>self.x-self.width*1/2 and x<self.x+self.width*1/2 and y>self.y-self.height*1/2 and y<self.y+self.height*1/2:

把上面的点的条件判断式代码放入到onmousepress方法中,满足条件后执行explode的对象消除方法。代码如下。

import cocos
import pyglet
class Fish(cocos.sprite.Sprite):
    def __init__(self):
        textures=[]
        for i in range(1,11):
            name_i="0"+str(i) if i<10 else str(i)
            fish_name_i="textures/fish04_"+name_i+".png"
            texture=pyglet.resource.image(fish_name_i)
            textures.append(texture)
        animation=pyglet.image.Animation.from_image_sequence(textures,0.1)
        super(Fish, self).__init__(animation)
        self.position = 800,200
        self.swim()
    def swim(self):
        self.position=800,200
        self.do(cocos.actions.MoveTo((-20,200),8)+ cocos.actions.CallFunc(self.swim))
    def on_enter(self):
        super(Fish, self).on_enter()
        cocos.director.director.window.push_handlers(self.on_mouse_press)
    def on_mouse_press(self,x,y,button,modifier):
        if x > self.x - self.width * 1 / 2 and x < self.x + self.width * 1 / 2 and y > self.y - self.height * 1 / 2 and y < self.y + self.height * 1 / 2:
            self.explode()
    def explode(self):
        self.stop()
        self.kill()

上述代码运行后,只有点击的位置在鱼精灵的身上时,才会出现鱼精灵的消失。

五、大批鱼类精灵的游动

前面完成了一个鱼类精灵的游动后,鼠标点击到其身上以后,该鱼类精灵就消失了。现在我们产生若干条鱼类,并且产生的位置在纵轴上是随机的。这样鱼类在背景类进行实例化和添加时,需要调用循环结构添加多个鱼类,这些添加的鱼类将其鱼的样式变成多种多样的话,把其中鱼的种类的索引值传入,根据不同的索引值调用不同的鱼类。在鱼类的初始化函数中对纵轴的坐标进行随机,并赋值给鱼类的position位置属性。代码如下。

import cocos
import pyglet
import random
class Fish(cocos.sprite.Sprite):
    def __init__(self,index):
        index = "0" + str(index) if index < 10 else str(index)
        textures=[]
        for i in range(1,11):
            name_i="0"+str(i) if i<10 else str(i)
            fish_name_i="textures/fish"+index+"_"+name_i+".png"
            texture=pyglet.resource.image(fish_name_i)
            textures.append(texture)
        animation=pyglet.image.Animation.from_image_sequence(textures,0.1)
        super(Fish, self).__init__(animation)
        self.y=random.randint(10,480)
        self.position = 800,self.y
        self.swim()
    def swim(self):
        self.y=random.randint(10,480)
        self.position=800,self.y
        minutes=random.randint(2,8)
        self.do(cocos.actions.MoveTo((-20,self.y),minutes)+ cocos.actions.CallFunc(self.swim))
    def on_enter(self):
        super(Fish, self).on_enter()
        cocos.director.director.window.push_handlers(self.on_mouse_press)
    def on_mouse_press(self,x,y,button,modifier):
        if x > self.x - self.width * 1 / 2 and x < self.x + self.width * 1 / 2 and y > self.y - self.height * 1 / 2 and y < self.y + self.height * 1 / 2:
            self.explode()
    def explode(self):
        self.stop()
        self.kill()

class Background(cocos.layer.Layer):
    def __init__(self):
        super(Background,self).__init__()
        self.width,self.height=cocos.director.director.get_window_size()
        sprite=cocos.sprite.Sprite("textures/bg.jpg")
        sprite.position=self.width//2,self.height//2
        self.add(sprite)
        for i in range(2,11):
            fish=Fish(i)
            self.add(fish)
if __name__=="__main__":
    cocos.director.director.init(width=800,height=480);
    background=Background();
    main_scene=cocos.scene.Scene(background)
    cocos.director.director.run(main_scene)

程序中在Fish鱼精灵类方法中首先判断传入的index鱼的种类值是两位数还是一位数,如果是1位数,前面加上0变成为两位数,如果是两位数就正常输出index值。同时修改了文件名的组合方式,”fish”后面跟上传入的index值再跟下划线,后面就是各种鱼的游动状态图。产生鱼类坐标是使用语句random.randint(10,480),也就是在纵坐标的10-480之间产生鱼精灵在图中的竖向位置,将其值赋值给self.y,在重复调用游动方法的swim方法中也要随机产生鱼精灵在图中的竖向位置赋值self.y,再把鱼精灵的初始位置赋值给self.position,并随机产生鱼精灵从屏幕右端游向屏幕左端的时间,这个时间做为MoveTo方法的周期。完成这些代码的调整后,在背景类中循环产生鱼的种类,循环的值从2到11其目的是资源库中是从第2种鱼到第10种鱼。

代码运行后的结果如下图所示。

代码的github地址:https://github.com/wawacode/cocos2dfishgame

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值