审判者
究竟怎样的人生才能让人喜欢上命运这个词
- 精华
- 5
- 帖子
- 11208
- 威望
- 10 点
- 积分
- 12714 点
- 种子
- 8 点
- 注册时间
- 2005-2-14
- 最后登录
- 2024-11-22
|
楼主 |
发表于 2012-3-15 23:51 · 湖北
|
显示全部楼层
本帖最后由 tring 于 2012-3-18 03:58 编辑
=================================
第〇章:写程序不是什么难度活
这是个给压根没写过程序的人看的伪入门章节,请有程序基础的你无视就可以了……哦不,请别完全无视,至少看看红字。
电脑不是什么聪明货,所谓的程序,就是一串告诉他要顺着做啥的命令。在早期的面向结构(如果不知道这是啥请别管了)的编程中,尤其是像PUTI所使用的早期BASIC中,上面那句话简直可以说就是完全字面意义的表达了程序的全部含义。
如果你完全没有接触过编程,那么对于PUTI所使用的BASIC来说,请你只需要记得一点,电脑执行程序时所做的事情仅仅就是一条命令一条命令的顺序执行。
对于BASIC这种入门级高级语言来说,基础语法是很简单直白的。
比如说你看到一行表达式 FOO=BAR+233,那么意思就是让FOO这个变量的值等于BAR这个变量的值加上233,经过这条语句后,FOO的值就被改变了(当然也可能改变前后一样)。虽然很多编程教材上都要强调等号“=”在作赋值用途(即我上面的那个例子)和作判断用途时的概念完全不同,但是我认为既然是高级语言,请直接读出来凭自己的语感吧,别深究这些概念。
而对于命令,请这么理解。比如说你看到一条命令 GBOX 0,0, 223,223,那么意思就是让电脑画一个方块(GBOX命令),而后面的4个数字是告诉电脑他需要知道的4个参数。几乎所有命令都支持这种 命令字 参数1,参数2,... 的表达方式,当然有些命令的参数并不是都用逗号隔开而是有些更直观的表达,具体可以看说明书里的命令集。
对于函数,就更简单了。比如说你看到一条表达式 FOO=SIN(BAR) 意思很显然就是让FOO的值等于BAR的值求正弦。
2点需要注意的:
1,PUTI里的BASIC是大小写敏感的,默认状态下你敲进去的全部是大写字母,若非你确定明白你在做什么,请尽量不要大小写字母混用。
2,PUTI里的BASIC虽然不区分不同的数值类型变量,但是会区分数值和字符串变量。数值变量(即内容为一个数值)可以直接命名;字符串变量(比如S$="THIS IS A STRING"里的S$)名字必须以$符号结尾。
当然,凡事都有点例外,如果说电脑真的傻到只能顺着一条一条执行的话,那我们想做一个流程10分钟的游戏岂不是要足足写满能让电脑执行10分钟命令才行?
所以我们还需所谓的“流程控制命令”。太复杂的解释我也不想多说,这里我只提2个如果你不懂得可能接下来所有代码都没法看明白的命令。即“循环”和“判断”。
1,判断语句:所谓的判断语句,就是当你需要电脑根据不同情况做不同处理的时候所要用的命令。比如这个例子:
“ 如果 你今天买来的那个游戏超好玩 那么 赶紧上网发推荐帖去 否则 发帖大骂坑爹 顺便问候制作人全家”
这就是一个很典型的判断。“你今天买来的那个游戏超好玩”是一个假设条件,“赶紧上网发推荐帖去”是当前面的假设成立时需要执行的命令,“发帖大骂坑爹 顺便问候制作人全家”是当前面的假设不成立时要执行的命令。
在PUTI的BASIC中写出一个判断语句“IF U=250 THEN PAY=0 ELSE PAY=1000:BEEP”
值得一提的是在PUTI的BASIC中IF语句必须单行写完!绝对不支持分行写。所以看到上面的例子中我在ELSE后面执行的2条语句中间用一个:隔开,这个冒号是BASIC里用于分隔2条语句用的符号,像这种必须将几行语句写在同一行的时候就需要用冒号隔开。
2,循环语句:当你需要电脑重复做某一件事情的时候,就可以用到循环语句了。关于循环语句,有很多小细节可以讲,这里我只讲个最基本的例子:
FOR I = 1 TO 3
PRINT I, "TIMES"
NEXT I
注意到这是一个多行的例子了。FOR...NEXT...是在PUTI的BASIC中唯一能分行使用的一组语句。这里具体的意思是“对于变量I,第一次执行到FOR语句的时候让I的值等于1,之后每次执行到FOR语句的时候判断I的值是否比3大,如果没有,则继续执行下一行,如果比3大(即最小是4),则执行NEXT I之后的下一行;每次执行到NEXT I这行语句的时候都将他自己加1,并回到FOR语句。”
这里的执行结果就是打出以下3行字
1 TIMES
2 TIMES
3 TIMES
对于FOR语句的具体细节没啥必要深究,就记得从1开始TO的那个数字就是次数其实就可以了。
另外NEXT后面是可以省略变量I的。
啊……程序基础部分终于讲的我觉得没什么继续深究的价值了,接下来的所有细节语法我都会在例子里出现的时候一起解释。
之后的内容将全部是游戏相关的了,不要指望我再来什么语言基础普及了……这东西真是又累人又没趣……
关于PUTI有什么问题,请直接先看说明书,如果说明书上没说明白的或者没看懂得的,
可以直接底下回帖问我,不管是软件使用方面的,还是程序方面的,我都会尽量帮你找到答案。
次回预告
今天时间不早了,从下次正式开始 第一章:让我们弄点像游戏的东西出来吧
=================================
第一章:让我们弄点像游戏的东西出来吧
作为这个帖正文的第一章,今天开始以游戏为第一主题了。说点题外话,就是我一直自认鸟语稀烂,所以之后的代码里如果有什么注释打印拼写语法错误的还请勿见怪。
或许你要问,为啥这章我们只是要弄点像游戏的东西,而不干脆弄个游戏出来。你见过第一章就去打魔王的RPG么?当然一开始就要以可爱(?)的史莱姆,波利之类的像是怪物的东西开始嘛。
那么既然是要做点像游戏的东西,首先就要搞明白的是游戏究竟需要像什么样子。
首先,由于一个游戏的程序,除非玩家想退出,我们需要他能一直运行下去。因此,我们首先需要的是一个无限循环,这样就能让程序一直运行而不退出了。
- '===MAIN LOOP===
- @MAIN
- GOTO @MAIN
复制代码 这里稍微做点规定,以后我一般会把对代码框内所有代码的解释和说明放在代码框的下方。
以上这段代码,虽然只有3行,但是我还是要逐行解释一下。
1,第一行的文字全部在'号之后,这叫做注释,而BASIC语言的注释符正是'号。注释中的内容并不会被执行,你可以在里面写上任何东西。但是惯例上,注释的作用是用来提示代码的功能。一般来说从行首开始的注释用于解释其下方的代码;而放在代码末尾的注释,比如“ CNT=CNT+1 'Counter for something” ,则是用于解释其前方的代码。不过当然这些都只是惯例而不是规定,你可以在注释里写任何东西,一切都不会被执行。
2,第二行以@符号打头。该符号表示行标(Label),用于表示程序中的一个任意位子(该行标所在之处)。虽然只是标志位子没有任何意义,但是结合其他的语句就可以实现改变程序流程的跳转功能了。
3,第三行的GOTO就是前面说的跳转语句。GOTO的含义是无条件跳转执行,也就是说无论如何都将执行跳转。而跳转的目标则是一个行标。这里使用前面定义下的行标@MAIN,就可以实现一个无限循环的跳转了。这段程序执行将无限的在第2行和第3行之间往返执行。
当然,只是一味的循环好比原地打转,毫无意义。那么怎样让这个原地打转的无限循环变的有点游戏的感觉呢?
在大多数谈对游戏一般认识的理论里,认为可以将一个游戏程序抽象成一个有限状态机。至于什么是有限状态机,这个我不打算真的从头来说概念,其实我自己都不是很明白这是个啥玩意。开头也说了,以后的主题一直是游戏,那么我就从游戏的角度来说下这个所谓的有限状态机是个啥玩意。
在大家一般的直观认识,若想把游戏抽象成一个概念会是什么样子的?
玩家输入---->[游戏程序处理]---->画面音效输出
以上认识应该来说是很普遍的吧。不过这个认识并不是很准确。主要有2点:
1,并不是仅当玩家有输入的时候,才有画面音效输出。所以游戏并不是仅仅将玩家的输入处理成为输出。
2,并不是玩家进行一样的输入的时候就有一样的输出。所以游戏除了依靠玩家的输入决定输出外,还要依靠什么别的东西。
那么把以上这个概念更精确一点就变成了以下这个样子:
玩家输入---->[游戏程序处理]---->(改变游戏内部状态)
(当游戏内部处于某种状态)---->[游戏程序处理]---->画面音效输出
请注意,上下2行的圆括号()里的内容并不等价,如果等价的话就变成了前面的那种比较粗糙的模型了。
而这个内部状态改变的机制就是所谓的有限状态机了。
假设现在有1个对象,现在该对象有3种状态,分别为状态1~3,那么考虑下表格:
当前状态\输入\改变后状态 | 状态1 | 状态2 | 状态3 | 状态1 | - | 输入1 | X | 状态2 | X | - | 无需输入 | 状态3 | 输入1 | 输入2 | - |
上面这个表格就是一个描述内部状态改变的表的例子。表格中填写进去的是当处于不同状态时对输入的不同反应。
比如这例子里的描述是:
当对象处于状态1时 遇到输入1 就会变成状态2
当对象处于状态2时 就会变成状态3
当对象处于状态3时 遇到输入1就会变成状态1 遇到输入2就会变成状态2
这时候如果定义 当内部对象处于状态2时 程序就会输出一个画面效果和一个音效
那么就完整的完成了一个类似上述2行从输入到输出的过程。
该过程中不会存在之前的粗糙描述中提到的2个问题。
真正的游戏,便是一段处理这种状态改变的程序,但是他更加复杂。游戏程序对于其中的每一个对象,都维护一个他自己的状态机。这个状态机包括一个记录当前状态的状态池,一段处理输入导致状态变化的代码,和一段对于不同的状态给予输出的代码。当各式各样的对象组合起来,就形成了一个完整的游戏。
理论部分就此结束,下面我们继续以代码示例来说明这个结构。
首先要做的,就是决定我们的这段程序里到底需要组合哪些对象。由于这章我的目的仅仅是介绍一些游戏基础知识,所以这里不准备弄的很复杂。不过再简单,以下的对象也是必须的:
1,玩家对象,这个对象由玩家的输入改变状态
2,NPC对象,这个对象的状态并不由玩家输入改变,而是由专门的AI代码来改变。这次的例子里我准备只加入1个该对象,扮演敌人的角色。
3,系统对象,这个对象用于维护系统运行时的相关信息。其实称之为一个对象确实不妥,通常他是一系列对象的组合,相当于游戏里的“世界”。这个对象的状态由专门的一套规则来改变,而这套规则就是所谓的游戏规则。这里我为了简化,也仅仅当其为一个单一对象。由于画面音效输出也可以认为是“世界”的一种表现方式,因此我简单的把输出处理交由了这个对象的处理代码来做了。
回到代码中,我们刚才有了一个无限循环,现在就要开始在这个循环的基础上添加内容了。
首先,我们需要对我们的每个对象状态进行处理,因此先给每个对象划分一块地方放处理状态的代码。
其次,我们需要保存每个对象的当前状态,因此给每个对象分配一个变量用以存放当前状态。
于是代码就变成了这样
- '===FOR INIT===
- STATPLA = 0
- STATNPC = 0
- STATSYS = 0
- '===MAIN LOOP===
- @MAIN
- '===FOR PLAYER===
- '===FOR NPC===
- '===FOR SYSTEM===
- GOTO @MAIN
复制代码 在这代码里,其实只是比先前的多了3个变量初始化,但是仅仅是加了几条注释给将要添加的代码标注一下,对于程序整体结构来说也是有意义的。
注意到我这里给3个用于保存对象当前状态的变量赋值都是0。这里的0代表的是对象的初始状态,通常来说就是所谓的闲置(IDLE)状态。若不是这个BASIC不支持使用宏的话,我使用一个有名字的宏来给变量赋值,而不是直接使用数字0,因为对于其他看到这段代码的人来说,他很难直接判断出这里的0到底代表的是什么状态。
大纲定下来后就是一点一点往里面填东西了,由于游戏最重要的是和玩家互动,所以这里我首先往里面填入的是玩家对象的状态处理代码
- '===FOR INIT===
- STATPLA = 0
- STATNPC = 0
- STATSYS = 0
- '===MAIN LOOP===
- @MAIN
- '===FOR PLAYER===
- BTN = BUTTON()
- IF STATPLA != 0 THEN GOTO @PLABYPASS1
- 'X BUTTON ON CLICK
- IF BTN AND 64 THEN GOTO @FIN
- 'A ...
- IF BTN AND 16 THEN STATPLA = 1
- 'B ...
- IF BTN AND 32 THEN STATPLA = 2
- 'Y ...
- IF BTN AND 128 THEN STATPLA = 3
- @PLABYPASS1
- '===FOR NPC===
- '===FOR SYSTEM===
- GOTO @MAIN
- '===FOR THE END===
- @FIN
- PRINT "GAME OVER"
- 'THIS IS FOR DEV
- SAVE "CH01"
- END
复制代码 这里从FOR PLAYER的第一行开始说明。BUTTON()是一个系统函数,其作用是用来返回3DS的当前按键状态。这个函数是可以带参数使用的,参数分别决定了几种不同的判断按键的方式,由于这里只是个演示例,所以不做特别要求,不带参数便是默认的按下就算按了。BUTTON()函数的返回值是一个整数。以2进制表示的话,其每一个2进制位对应一个按键,如果按键被按下了,那么他对应的那一位就会从0变成有值。详细的按键对应可以去看软件说明书。这里我只说下我用到的4个键 A,B,X,Y分别对应的值是16,32,64,128。因为每次调用BUTTON()函数得到的都是当前瞬时的按键状态,所以为了处理一致,我们仅使用当前这个时间的BUTTON状态用作之后的所有处理,将BUTTON()函数的返回值赋给变量BTN,之后就仅使用BTN变量来判断键值了。
下一行是很重要的一个判断,不过详细说明之前,我先为昨天的一个错误道歉并更正。昨天说道“=”等号的时候,提到过不要在意赋值和判断的区别,这是我完全搞错了……在后来的BASIC版本里虽然“=”等号既可以表赋值也可以表判断,但是在这个比较早期的版本里“=”只能表赋值,判断必须要用“==”双等号表示表示。
言归正传,这里的这行判断是用来判断玩家对象的当前状态的。刚才前面介绍概念的时候也说了,状态机对于不同输入的不同反应是要取决于当前状态的。因此这里必须先对当前状态进行确定后才能对输入进行处理。
这里的判断我之所以用否定式“!=”(不等于),原因在于在前章提到的,这个BASIC里的IF语句是不能分行写的。可是这里我有大量的工作需要放在判断成立后的执行里,这里就相反让其如果不成立就跳过我的这些代码。因此我的条件写为了,如果玩家的当前状态不为0(IDLE)就跳过之后的那部分,意思是那部分代码只有当玩家状态为0(IDLE)的时候才执行。
而这部分只有在IDLE时才执行的代码按照前面的理论,作用就是根据玩家的输入,改变对象的状态。
注意到连着几行都是以“BTN AND XXX”为判断条件的。这里有一点要相当注意:在BASIC里“AND” “OR” “NOT” “XOR”这几个命令都是按位逻辑操作,并非真假逻辑操作,而这个BASIC里并没有真假逻辑操作符。举个例子,假设用“TAND”代表真假与运算,那么“16 TAND 3”的结果就是“1”,也就是真,因为16非0为真,3也非0为真。而“16 AND 3”的结果就是“0”了,因为16和3的按位与正好将全部1都错位开,结果就成了“0”。也就是说当你使用“A AND B”来作为判断条件的时候代表的意思绝对不是“A非0,并且B非0”,而是“用B作为掩码,判断A相应位置是否被置位”。不过如果用“==” "<"等关系判断符的话,由于其结果只有0和1,因此此时这几个位操作符也能作为真伪逻辑判断来使用,比如“A==0 AND B>3”的意思就是“A等于0,并且B大于3”
因此这里的每一行判断,都是在判断之前读到的按键为什么。如果你问为什么不把条件写作“BTN == 16”而要写作“BTN AND 16”,那就说明你已经入门了。因为BUTTON()返回的值可能并不是一个单键值,如果这时候玩家同时按了A键和B键位,那么返回值就是“16 OR 32”也就是48了。这时候如果你用“BTN==16”作为判断条件,那么系统会认为A键并没有被玩家按下,而你用“BTN AND 16”来判断,则可以很顺利的由48 AND 16 = 16,非0,来判断出A键被按了,而且还能通过“BTN AND 32”来判断出B键同时也被按下去了。这对于允许多键同时按下的游戏非常重要。
因此这4行代码分别代表着如果X,A,B,Y这4个键分别被按下的情况。当A,B,Y被按下的时候,我分别将玩家对象的当前状态值改变为1,2,3,这代表着玩家状态此时已经因为玩家的输入发生了变化。
而X这个键被按下后,我让其跳转到了一个叫@FIN的LABEL处。这个LABEL是在主循环的外面,我新加入的一段代码,其作用就是用来结束游戏。我之前也说了,主循环是一个无限循环,如果不是玩家希望退出,是不会退出的。因此我们就必须给玩家留一个退出游戏的方法,这里我简单的写作只要玩家按X键就退出游戏。
看看@FIN这里的代码,第一行是个简单的输出打印,用于在屏幕上打印一行“GAME OVER”的字样,毕竟游戏结束还是要给玩家一点提示的嘛。
第二行是一个我为了我写代码编辑方便而加入的临时语句,SAVE的作用是将当前代码保存当后面指定的文件里,这样每次退出游戏就会自动保存我当前编辑的代码,而不需要我在执行窗口手动敲SAVE命令,这算是一个编辑代码的小心得。但是如果你只是执行现成代码而不需要编辑,大可将这行去掉。
第三行就是结束程序的命令了。
对于玩家对象的状态改变,我只设置了在状态0(IDLE)时根据输入按键,变化为状态1,2,3的这段规则。对于状态1,2,3我并没有设定其状态转变。至于这3个状态分别代表什么意思,请暂时不要在意,因为这只是个像游戏的东西,并不是游戏,所以这里仅仅当作是玩家选择的3种不同动作吧。
这样,关于玩家对象的操作代码就完成了。
接下来是NPC对象的状态转换代码,由于并没有对其他地方代码进行修改,我就只贴上这部分。
- '===FOR NPC===
- IF STATNPC==0 THEN STATNPC = RND(3)+1
复制代码 由于我这里将NPC设定为一个敌人,我希望他与玩家在公平的环境下对抗,因此我将其可以变成的状态定为和玩家对象一样的 状态1,2,3 这三个。
惯例的,一开始还是先判断自身的当前状态,如果NPC对象的当前状态为状态0(IDLE)才进行之后的状态转换操作。由于NPC的状态改变并不依赖于玩家的输入,所以这里我并不去读取判断按键输入。
注意到状态转换操作只有很简单的一句话“STATNPC = RND(3)+1”。RND(N)这个函数的意思是返回一个从0到N-1的随机整数,也就是说RND(3)的返回为从0到2之间的一个随机的整数,可能为0,1或者2。在这个函数后面将其加1,使得这个随机数变成1到3,这样就正好是玩家对象可以转换到的那3种状态了。
NPC对象只需要这个很简单的状态转换就可以了,我依旧不打算给NPC对象的状态1,2,3设置状态转换规则。因此这样就算完成了。
接下来是系统对象的状态转化代码。由于我前面说到,这部分代码相当于整个游戏的世界观,也就是游戏规则,而且我为了简化还把输出部分也加入到了这个里面,因此代码会比前面的要复杂,我这里还是一步一步的填充完成,现在先只给出个框架。
- '===FOR SYSTEM===
- '"AND" "OR" ARE BOTH BITS LOGIC
- IF STATPLA<=0 OR STATNPC<=0 THEN GOTO @SYSBYPASS
- 'SYSTEM STAT
- 'OUTPUT ALL STAT
- 'RESET ALL STAT
- @SYSBYPASS
- VSYNC 1
复制代码 上来的第一句话,依旧是对当前状态的判断。不过和前面2个对象有点不同,在这个我简化的系统 对象中,他并不需要那么关心自己的当前状态,所以我很不规范的将“IF STATSYS!=0 THEN GOTO @SYSBYPASS”省略了,这种省略其实我并不是很推荐。
既然并没有判断自己的当前状态,那么第一行的状态判断是什么意思呢?其实就是说,如果玩家对象或者NPC对象在闲置状态(状态0 IDLE),那么系统也什么都不做就直接跳过。事实上玩家也不大乐意在自己什么都没做的时候系统会输出个不停,还一直改变状态。细心的人可能会发现,这里我用的“<=0”来判断对象状态,而不是“==0”,这是因为我在之后会用到小于0的状态表示一些其他含义,所以这里的代码就加上了小于判断。
注意到在系统代码的最后一行,我写上了一个VSYNC 1命令。这个命令是用于游戏的帧数同步的。前面也说到游戏的主体是一个大的无限循环,而这个无限循环如果不做任何限速,将会以CPU的最高速度运行,因此输出的画面将会完全失控。通常上游戏程序里一次主循环刷新一帧画面,因此需要同步上主循环的执行时间和帧率。这个VSYNC命令就是做这个用的,其后面的数字表示需要以几帧为单位同步。硬件帧率是60帧每秒,因此要想把游戏控制在60帧的速度执行而不超速,这里使用VSYNC 1就可以实现。相应的如果用VSYNC 2就是30帧,依次类推。这行语句一般放在主循环的最末尾,保证无论走的是什么分支,每一次主循环都会执行一次该语句。
对于系统对象的主要部分代码,我现在只是将他分成了 状态转换,输出,重置状态 这3个部分,之后逐项往里填写。
首先是系统对象的状态切换,也就是核心游戏规则部分。
- 'SYSTEM STAT
- COND = STATPLA - STATNPC
- IF COND==1 OR COND==-2 THEN STATSYS = 11
- IF COND==2 OR COND==-1 THEN STATSYS = 12
- IF COND==0 THEN STATSYS = 10
复制代码 这***据玩家状态和NPC状态,进行了一个简单演算得到了3种系统对象状态。或许聪明的你已经发现了,这不就是个标准的剪刀石头布逻辑么……玩家状态和NPC状态的123分别代表出的3种符号,而系统状态的11,12代表输赢,10代表平局。虽然我知道你懂了,但是我当然不会弄个剪刀石头布来忽悠你了,接下来的输出代码让你看到对游戏的执着。
这里的输出代码的主要做的事仅仅是将数字的状态,翻译成文字,然后打印在屏幕上。由于3个对象的状态我使用了相同的规则定义,因此实际上对于3个不同对象的状态翻译是能以相同的代码完成的。因此我这里稍微做了点小处理,加了个所谓的“子程序”
这部分是输出部分的主代码
- 'OUTPUT ALL STAT
- PRINT "======="
- PRINT "PLAYER CHOOSE",
- A1 = STATPLA
- GOSUB @TRANSSTAT
- PRINT "NPC CHOOSE",,
- A1 = STATNPC
- GOSUB @TRANSSTAT
- PRINT "GAME RESULT",,
- A1 = STATSYS
- GOSUB @TRANSSTAT
- PRINT "======="
复制代码 代码仅仅是简单的将3个对象的当前状态打印出来,但是如果真的是简单的打印,状态1,2,3,这玩意哪里像游戏了?!谁TM懂得1,2,3是个啥意思啊。因此我还调用了“子程序”@TRANSSTAT来做点小翻译,使他看起来更像游戏一点。
这段代码的最大要点是变量A1!看得到我一直只有给A1赋值而没有使用过。
这里不得不说道早期BASIC的GOSUB命令。所谓的GOSUB虽然看起来像是对子程序的调用,但是其实重点不是“SUB”而是“GO”……。在那个年代的BASIC中并没有任何关于 子程序 函数 调用 等这些东西的概念,GOSUB只不过是比GOTO多了一个小功能,那就是他能在跳转之后通过RETURN语句返回到跳转之前的下一条语句。形象点说就是跳出去了还能跳回来。虽然对于程序流程来说这样就相当于是个对子程序的调用了,但是问题在于这种所谓的“调用”完全没有任何机制传递参数,返回结果。
因此我这里特地保留A1,A2,A3,A4,A5等变量名专门用于传递参数,而使用RSLT变量来返回结果,因为早期BASIC的所有变量都是全局的(其实根本没有域的概念就谈不上全局和局部了)。
所以这里我给A1赋值,实际意义就是将参数传递给后面的那个“子程序”@TRANSSTAT。
接下来这部分是所谓的“子程序”,我其实把他放到了@FIN的后面,因为无论放哪里,只要不影响其他代码的执行流程,都无所谓。
- '===FOR SUB ROUTINE===
- 'TRANSLATE STATS AND OUTPUT
- @TRANSSTAT
- TRANSSTR$ = ""
- IF A1 == 1 THEN TRANSSTR$ = "FIRE"
- IF A1 == 2 THEN TRANSSTR$ = "WATER"
- IF A1 == 3 THEN TRANSSTR$ = "ICE"
- IF A1 == 11 THEN TRANSSTR$ = "PLAYER WIN"
- IF A1 == 12 THEN TRANSSTR$ = "NPC WIN"
- IF A1 == 10 THEN TRANSSTR$ = "DRAW"
- 'OTHER STAT CAUSE A ERROR
- IF TRANSSTR$ == "" THEN GOTO @ERR
- 'OUTPUT TRANS RESULT
- PRINT TRANSSTR$
- RETURN
复制代码 代码是很简单的判断参数A1的值,然后输出一个字符串,甚至没有返回值,单纯的只是做了一个翻译工作。至于那个状态的名字,正如我刚才所说的,这并不是剪刀石头布那么没营养的东西,这是RPG标准的属性相克!火克冰,冰克水,水克火。
虽然简单,这段代码里还是有亮点的,那就是如果说传入的参数A1并不是能翻译的那6种状态之一,比如是状态0(IDLE),不但不做任何翻译,而且还会直接跳转到统一的错误处理流程。使用统一的错误处理在任何一种编程语言里都是一个非常好的习惯。
对于正常翻译并显示的状态,代码只是很简单的用RETURN将其返回。
以下是统一的错误处理代码
- '===FOR ERRORS===
- @ERR
- PRINT "AN ERROR OCURED!"
- END
复制代码 这里我并没有做多复杂,只是打印一个错误发生,并且结束程序而已。这段代码放在了文件的最末尾。
最后一部分代码用于重置这3个对象的状态,相当于让玩家再开一局。
- 'RESET ALL STAT
- STATPLA = 0
- STATNPC = 0
- STATSYS = 0
复制代码 代码写到这里的话,基本上已经实现了全部的功能了:状态转换,输入处理,输出处理。但是如果你试着运行这段程序的话就会发现结果并不如期。
理想上的结果应该是,当玩家按下一次按键后,系统进行一次裁判,决断出是玩家胜利还是NPC胜利。
可是实际结果却是,当玩家按下一次按键后,连着出现了若干次结果判定,而且每次NPC出的招数都不一样。
会出现这种理想与实际的偏差,原因在于按键。由于主循环是以每秒60次的满速执行的,所以玩家的一次按键,程序可能会连续在若干次主循环里捕捉到,简单来说就相当于按键被按下了却还没弹起来,就变成了连发状态了。
这里我为了解决这个问题,给玩家对象加入了一个新的状态,由于这个状态并不是游戏规则里的一部分,不实际参与判断输赢,而仅仅是对玩家状态做一个微调,我使用了负数“-1”(这就是为什么我在系统状态代码里判断玩家状态时加入了小于0的判断)。而这个状态-1代表着,此时请玩家对象等待按键被放开。而在系统状态代码最后的重置状态时将玩家的状态由原本的状态0(IDLE)改成了状态-1,等待按键回中。
- 'RESET ALL STAT
- 'SET PLAYER STAT FOR WAIT BUTTON UP
- STATPLA = -1
- STATNPC = 0
- STATSYS = 0
复制代码 因为加入了新的状态,就需要在玩家对象的状态转换规则中加入新的规则。
- '===FOR PLAYER===
- BTN = BUTTON()
- IF STATPLA != 0 THEN GOTO @PLABYPASS1
- 'X BUTTON ON CLICK
- IF BTN AND 64 THEN GOTO @FIN
- 'A ...
- IF BTN AND 16 THEN STATPLA = 1
- 'B ...
- IF BTN AND 32 THEN STATPLA = 2
- 'Y ...
- IF BTN AND 128 THEN STATPLA = 3
- @PLABYPASS1
- IF STATPLA != -1 THEN GOTO @PLABYPASS2
- 'BUTTON UP
- IF BTN == 0 THEN STATPLA = 0
- @PLABYPASS2
复制代码 这里对于状态0的规则没有任何改变,但是加入了对于状态-1的处理。当状态为-1的时候,如果按键放开(BTN为0),则转换为状态0(IDLE),否则继续保持状态-1等待。
至此,这章的例子的全部代码就完成了。
- '===FOR INIT===
- STATPLA = 0
- STATNPC = 0
- STATSYS = 0
- '===MAIN LOOP===
- @MAIN
- '===FOR PLAYER===
- BTN = BUTTON()
- IF STATPLA != 0 THEN GOTO @PLABYPASS1
- 'X BUTTON ON CLICK
- IF BTN AND 64 THEN GOTO @FIN
- 'A ...
- IF BTN AND 16 THEN STATPLA = 1
- 'B ...
- IF BTN AND 32 THEN STATPLA = 2
- 'Y ...
- IF BTN AND 128 THEN STATPLA = 3
- @PLABYPASS1
- IF STATPLA != -1 THEN GOTO @PLABYPASS2
- 'BUTTON UP
- IF BTN == 0 THEN STATPLA = 0
- @PLABYPASS2
- '===FOR NPC===
- IF STATNPC==0 THEN STATNPC = RND(3)+1
- '===FOR SYSTEM===
- '"AND" "OR" ARE BOTH BITS LOGIC
- IF STATPLA<=0 OR STATNPC<=0 THEN GOTO @SYSBYPASS
- 'SYSTEM STAT
- COND = STATPLA - STATNPC
- IF COND==1 OR COND==-2 THEN STATSYS = 11
- IF COND==2 OR COND==-1 THEN STATSYS = 12
- IF COND==0 THEN STATSYS = 10
- 'OUTPUT ALL STAT
- PRINT "======="
- PRINT "PLAYER CHOOSE",
- A1 = STATPLA
- GOSUB @TRANSSTAT
- PRINT "NPC CHOOSE",,
- A1 = STATNPC
- GOSUB @TRANSSTAT
- PRINT "GAME RESULT",,
- A1 = STATSYS
- GOSUB @TRANSSTAT
- PRINT "======="
- 'RESET ALL STAT
- 'SET PLAYER STAT FOR WAIT BUTTON UP
- STATPLA = -1
- STATNPC = 0
- STATSYS = 0
- @SYSBYPASS
- VSYNC 1
- GOTO @MAIN
- '===FOR THE END===
- @FIN
- PRINT "GAME OVER"
- 'THIS IS FOR DEV
- SAVE "CH01"
- END
- '===FOR SUB ROUTINE===
- 'TRANSLATE STATS AND OUTPUT
- @TRANSSTAT
- TRANSSTR$ = ""
- IF A1 == 1 THEN TRANSSTR$ = "FIRE"
- IF A1 == 2 THEN TRANSSTR$ = "WATER"
- IF A1 == 3 THEN TRANSSTR$ = "ICE"
- IF A1 == 11 THEN TRANSSTR$ = "PLAYER WIN"
- IF A1 == 12 THEN TRANSSTR$ = "NPC WIN"
- IF A1 == 10 THEN TRANSSTR$ = "DRAW"
- 'OTHER STAT CAUSE A ERROR
- IF TRANSSTR$ == "" THEN GOTO @ERR
- 'OUTPUT TRANS RESULT
- PRINT TRANSSTR$
- RETURN
- '===FOR ERRORS===
- @ERR
- PRINT "AN ERROR OCURED!"
- END
复制代码 或许到这里你还是会觉得我忽悠你,弄的这么复杂,不过是个改了名字的剪刀石头布的程序。
请回头去看看这章的标题,这不过是个像游戏的东西而已,我之所以拿出这么个简单的例子来,就是为了介绍一个游戏的最基本原理和架构。这段代码虽然实现的功能异常简单,甚至你可以用比这短几倍的代码实现完全相同的功能。
但是这段代码的最大意义在于,他的结构是可以扩展的,你可以为其扩展更多新的对象,对每个对象扩充更多新的状态。
一款复杂的游戏就是从这么一个简单的例子中开始的。
**************
附录:代码下载
在给出下载前我先说两句,这次的文件交换功能还是弄得很折腾:
1,只能从PUTI中导出文件到SD卡上,不能从SD卡上导入文件入PUTI中
2,只能通过QR CODE导入文件到PUTI中,无法将PUTI中的文件直接导出成QR CODE
因此交换程序的流程如下:
1,将你的文件从PUTI中导入SD卡
2,将SD卡插入电脑,拷出刚才导入的文件
3,登录官网:http://smileboom.com/special/ptcm2/qrmaker/ 使用该工具将你刚拷出的文件转换成QR CODE
4,将QR CODE公布出来给别人。
导出的文件头部分是BIN的,但是后面的部分是标准TEXT的,所以实际可以用任何16进制编辑工具(比如UE)打开文件去掉头,结果就能直接作为文本方式查看程序代码了。
以下是我本章所使用的例子的QR CODE图片打包文件
当然我还是直接把图贴上,不过由于A9的图床会缩分辨率,不知道还能不能认得出来,认不了就直接下压缩包吧
**************
××××××
吐槽:
这次的文字量真的比想象中的大的多……本来以为这么简单的小程序可以很轻松搞定的,没想到说了这么多废话……
下章考虑适当缩减文字比例了。否则看这状态似乎不是周末根本没时间弄啊。
次回预告:
第二章:我要的是能动的画面
×××××× |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|