Python中的pygame精简部分-下

QQ技术分享交流互助群:

QQ群号:566341887   (2024/5刚建的群,可能没什么人一开始,相信会越来越好哒。大家一起互帮互助,共同进步,交流分享,奔赴未来!)

根据“上”和“下”,我们已经可以粗略地构建一个游戏了。这一篇,我们需要更加完善游戏,对一些边边角角进行补充。

一.状态机

这个东西哩,如果你比较有个性的话,或者是游戏不太需要的话,也没必要看。

通俗点来说,就像是菜单一样的东西,清楚地,列个表。

从图中看,根据状态机的状态标记不同,窗口中展现的游戏状态也不同。不但有登录,也有主页,甚至可以有角色,战斗,剧情……以此类推。

把它转化为程序的话,可以理解为对于一个自变量mark,不同的mark值对应了不同的界面,使整个程序变得更加整洁,后期改BUG也方便许多。

mark=0

while True:
    if mark==1:
        #登录
    elif mark==2:
        #主页
    elif mark==3:
        #角色
    elif mark==4:
        #商店
    #......以此类推

大概上就是这么个意思。

当然,mark在玩家的选择中,也会改变mark的值,因此达到不同状态的效果。

二.大地图

1.小区块问题

前面的游戏中,玩家都是在一个固定的区域中活动,相当于一个关卡,如果说许多个这样的区块构成一个大区块,从而在一个较大的区块中运行,那么就会有一个问题非常影响游戏体验。

比如这4个区块合在一起,黄色的区块为玩家所处的区块,红色和紫色都是看不到的区块,在游戏中为了流畅性,各个区块之间不存在黑色的条条框框。那么,问题来了,玩家在黄色区块中,假设紫色区块中存在可以远程伤害的敌对生物,玩家在敌对生物的攻击范围中。照理来说,怪物应该对玩家发起攻击,但是玩家又看不到怪,就是说,不是很连贯,有种断开的感觉。

2.大区块思路

解决这个问题其实很简单,把玩家固定不动,移动背景和怪物等。相对,就是说相对喵,理解吧。

本来是,玩家往右走,玩家的x值增加,相反的,要改成背景的x值左移,包括怪物和其他实体的移动。玩家下落改成背景、怪物和实体上移。

所以嘞,对于一个800×600的窗口,背景可能就需要做到3000×2000。而且不能把背景的最左上角的点放于窗口的(0,0),不然玩家一旦一开始往左移动一下就出现问题了。

3.大区块代码

backx,backy=50,50 #背景的坐标

while True:
    keys=pygame.key.get_pressed()
    if keys[pygame.K_d]:
        backx-=5
    if keys[pygame.K_a]:
        backx+=5

其中一部分差不多就这样啦,当然,熟能生巧,其他部分就自己写啦。跳跃部分也一样的。

(敌对生物移动时,这样写不会对其产生影响,只需要在移动时,调用类中的一个函数,移动保持与屏幕一致。)

(1)举例

讲讲可能,额,就是说,不好理解,啊啊啊啊啊啊,烦死了,慢慢讲。

(2)慢慢讲

从问题——>思路——>方案。

①问题

问题呢,在于玩家移动时,生物保持自己原有的移动,又要时玩家的移动对生物产生影响,体现出玩家在移动的效果。

②思路
思路一

假设敌对生物在往左移动,x持续减小,x-=1,玩家一按下"D"键,相对于玩家要左移,生物要右移,那么当玩家按下之后,生物的x-=5(假设玩家每步长为5)二者要连贯在一起,就要分开成两个def来写,互不干扰。

一个def用于生物自己原本的运行,另外一个def用于玩家对生物的干涉,从而二者叠加,不会造成冲突。

思路二

本质上差不多,就是为了保证生物本身与玩家干涉不冲突。

生物移动自己原本x数值,除此之外还保持x+=dx,dx为屏幕移动距离,若玩家不按移动键,则dx=0,屏幕既不会移动,生物也照样保持原有移动。如果dx有了数值,x本身的改变和dx的数值二者叠加,导致出一个综合性的效果。

③方案

理清了思路,我们就是说直接上代码。

(以下为本喵从自己乱写的一个游戏中的举例,运用的是思路二)

class enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image=pygame.image.load("P/enemy/enemy.png")
        x=randint(winflagx,5000)
        self.rect=self.image.get_rect(center=(x+25,by+540))
    def update(self):
        self.rect.x-=randint(2,5)
        self.rect.x+=dx
        self.rect.y=by+540

咱们就是说主要看update部分。

        self.rect.x-=randint(2,5)
        self.rect.x+=dx

可以看到,这两行分开来,互不干涉,最后结果综合。

(3)静态物品

同理,根据上面的部分,删掉物品自己本身的移动就行了。

比如,以下。

class dyh(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image=pygame.image.load("P/tool/7.62dyh.png")
        self.rect=self.image.get_rect(center=(waitx,waity))
    def update(self):
        self.rect.x+=dx
        self.rect.y+=g

三.帧率

这个玩意,相当影响游戏体验感的呀喵。

而且还可以从中反推出运行一遍程序,所需要的时间,以及关系到一些现实中与游戏中时间的关联。

1.例子

zf=pygame.time.Clock()

while True:
    zf.tick(30)

主要就这么两个,但是注意,这个"zf.tick(30)"必须放在while里面,放外面没用(帮你们试过了已经)

2.影响

对于之前写的代码,如果你们照着写下来,运行的时候就会发现,这个速度很快啊,所以说我数值才那么小。加上这个之后完全就不一样了。

3.意义

那么对于上面这个例子,是什么意思呢。

zf.tick(30)

1s之内,该程序运行30遍,也就是说30帧/s。

四.抽奖机制

这个的话,主打的就是靠randint随机函数。

嗯额……说实话没什么好展开讲的,先放个例子。

from random import randint

gailv=0

gailv=randint(0,100)
if gailv<=10:
    print("中奖啦")

以上中奖概率就是10%。更多的细枝末节,自己添些elif即可。

五.存档

对于一次性的游戏,就是那种开一次就要重来的那种,没必要这种存档。

1.思路

根据状态机,在登录界面时,输入相应账号和密码,如果在文档中有匹配的,那么读取紧随其后的数据,把那些游戏中的初始值代替掉。并且在游戏中,后来,即使保存创建新的存档,或者替换掉原来的存档,进行一个更新的措施。

2.(小插曲)

之前都是用C++写的文件读取,改写之类的。

python这玩意本喵自己就浅浅看了一下,不是很明白,所以说接下来讲的方法可能比较落后,若有更好的还请指出,谢谢。

3.创建存档

首先要有有一个.txt文档用来存数据。

先讲个前提:对于这种数据要有一个格式,我们假设为如下。

名称

密码

等级

金币

name=input("名称:")
mima=input("密码:")
f=open("档.txt","a")
duqu=open("档.txt","r")
li=-1
mark=0
for line in duqu.readlines():
    li+=1
    line=line.rstrip("\n")       #带上换行
    if line==name and li%4==1:
        mark=-1
    if mark==-1 and li%4==2:
        mark=1
if mark==0:
    f.write("\n"+name)
    f.write("\n"+mima)
    f.write("\n"+"0")
    f.write("\n"+"0")

这个里面f用于书写追加,duqu用于读取识别。mark=0则为判断没有该存档,进行自动创建存档,如果要改成报错也行,详细后面讲。对于新的账号,自动在文件档.txt后面进行追加名称、密码、等级(初始为0)和金币。

4.读取存档

当检测到存档中有玩家输入的名称,并且密码符合,那么就进行读取数据。

name=input("名称:")
mima=input("密码:")
f=open("档.txt","a")
duqu=open("档.txt","r")
li=-1
mark=0

level=0
money=0

for line in duqu.readlines():
    li+=1
    line=line.rstrip("\n")
    if line==name and li%4==1:
        mark=-1
    if mark==-1 and li%4==2:
        mark=1
    if mark==1 and li%4==3:
        level=int(line)
    if mark==1 and li%4==0:
        money=int(line)
        break

if mark==0:
    f.write("\n"+name)
    f.write("\n"+mima)
    f.write("\n"+str(level))
    f.write("\n"+str(money))

这个没什么多的可以讲的,就是这么个样子,给你们看一下效果。

5.保存数据

(额啊啊啊,本喵翻了书,翻了网,找不到可以修改.txt其中某一行的办法,那只能暴力解决啦!)

一般来说,指针不是在开头就是在结尾,那么我们还需要另外一个来存储原有文档,在碰到要修改的数据处写入修改后的数值。

(1)思路

1.因为要重新覆盖写,所以说在一开始就要对当初的.txt进行存档读取,把他们存放到一个列表里面。

2.为了后期for写入,我们需要一个变量li=-1用于存储。

3.在for中,为了把保存数据和抄写原有数据不冲突,我们另外要一个xieru=0作为标记。

4.当检测到原有存档中有与目前登录的名称相同时,则接下来的判断中,根据位置输入程序中的level和money的str形式,并且xieru=1。

5.每次for的结尾都追加一个if,如果xieru为0的话,就在文档中保存原有数据。因此每次for开头,都要先将xieru重置为0。

(2)代码

name=input("名称:")
mima=input("密码:")
duqu=open("档.txt","r")
li=-1
mark=0
level=0
money=0
xieru=0

cun=[]
for line in duqu.readlines():
    li+=1
    line=line.rstrip("\n")
    cun.append(line)
    if line==name and li%4==0:
        mark=-1
    if mark==-1 and li%4==1:
        mark=1
    if mark==1 and li%4==2:
        level=int(line)
    if mark==1 and li%4==3:
        money=int(line)
        mark=-2

if mark==0:
    cun.append(str(name))
    cun.append(str(mima))
    cun.append(str(level))
    cun.append(str(money))
    li+=4

fugai=open("档.txt","w")
for i in range(0,li+1):
    xieru=0
    if cun[i]==name and i%4==0:
        mark=2
        fugai.write(name+"\n")
        xieru=1
    if mark==2 and i%4==1:
        fugai.write(mima+"\n")
        xieru=1
    if mark==2 and i%4==2:
        xieru=1
        fugai.write(str(level)+"\n")
    if mark==2 and i%4==3:
        mark=3
        xieru=1
        fugai.write(str(money)+"\n")
    if xieru==0:
        fugai.write(str(cun[i])+"\n")

这个为了方便,我把一整串的代码全放上来了。

本喵的建议话,最好自己在草稿本上列个表,数据对应进去,纸头上算一遍。

(3)答疑

这个相对于前面的代码,有很多改动,我自己纸上算完,改动之后变这样的。下面的答疑呢,不可能面面俱到,只能说一下常见的,对于上述代码的。

疑1:为什么li的初始值为-1?

喵:因为列表的一个变量的位置在列表中为第0个,在第一个for开头中一开始就有li+=1,所以li=0时,刚好存储cun[0],二者相对应。

疑2: 为什么第一个for中,判断名称符合后,mark=-1?

喵:可能存在名称匹配,但是密码不匹配的情况。这样要满足两个条件,才能读取下方的level和money。

疑3:为什么money读取完之后,mark=2?

喵:因为要把所有的内容先读取保存到cun里面,读取到需要的数据,不是意味着后面的不要读取。 

哎,非常麻烦,这个东西没地方学模板,本喵搞了一整天才弄清楚,累死了……烦死了……经常出现数据串台,漏一个呀,多一个呀,乱存储。哎……自己好好看吧,不懂再问。 

六.撒花

非常好,看到这里,学完了算是,差不多已经可以做一个像样的游戏了,加油吧!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值