设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 27151|回复: 112
打印 上一主题 下一主题

[原创发布] 【14.7.5第二版更新】RGSS1脚本入门参考

[复制链接]

Lv4.逐梦者 (版主)

梦石
0
星屑
9532
在线时间
5073 小时
注册时间
2013-6-21
帖子
3580

开拓者贵宾剧作品鉴家

跳转到指定楼层
1
发表于 2013-10-12 22:24:57 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 RyanBern 于 2015-8-24 12:15 编辑

[box=RoyalBlue]
目录
[/box]

第0章节:预备知识1L(主楼)
第1章节:类7L
        √热心朋友的相关补充23L
第2章节:解密RGSS系统11L
第3章节:改动游戏对象14L
第4章节:窗口的使用19L
第5章节:场景的使用(一)20L
第6章节:场景的使用(二)21L
第7章节:尾声22L


写在前面
       一位论坛的朋友和我说,他看过很多RMXP脚本的教程,但是感觉没得到什么帮助,脚本也总写不好。我想这也是很多论坛朋友的共同问题吧,想自己弄个脚本,却无从下手;想看教程,却一头雾水(尤其是游戏里面的F1,感觉要有一定的基础才能理解最主要的部分)。这些天在论坛里面逛了逛,有不少尝试着自制脚本的朋友,但写出的脚本却总也通不过。我也看到了一些朋友写的代码,不得不说,代码中很多错误都是源于对教程的误解和对范例脚本(也就是游戏默认的脚本)的错误移植,游戏默认内置脚本其实是个很好的参考,但是如果不加分析胡乱利用一通,当然是不行的。于是我想到写下这个帖子,帮助那些渴望写出自己脚本的朋友们,成为一个真正的“脚本党”。我其实也算是个写脚本的业余爱好者,同时也在不断挖掘RGSS1更深层次的东西,本贴介绍的,仅仅是RGSS1的冰山一角,但却是我们编写脚本最常用的知识和基础。发出这个东西,不敢说能让大家都成为脚本高手,至少能让大家对RGSS1有个更清楚的认识。以下教程中,大家可能对Ruby和RGSS1有所混淆,Ruby是一门程序设计的语言;而RGSS1是基于Ruby编写的脚本系统,有很多特定的功能。

       这里写的是我对写脚本的一些理解,希望各位高手能积极提出意见,有哪里写得不对的地方,还请大家帮忙批评指正。另外大家如果遇到什么相关的问题,可以随时在帖子中询问,只要我有时间,我会立即为大家解答的。(当然求脚本之类的提问我在这里就不处理了,请移步RMXP 提问区)

配套脚本教学视频:https://rpg.blue/thread-381015-1-2.html

【第二版序】
       半年前这个教程杀青了,半年后再翻出来,感觉不是很满意,有些地方都没有说清楚。而且后面脚本解读那里,根本就是在利用“抄脚本——写分析”的模式,让人很难有欲望看下去。因此第二版会有一些改动。这半年来又接触了不少编程的技术,对一些问题的看法也和从前大大不同了,因此都写在这里,大家好好探讨一番。这次的改动,注重细节的解说,再加上理论与实践结合的部分(虽然还没有做,大家不要来BS我),想必能比第一版更好吧。


[box=RoyalBlue]
第0章节:预备知识
[/box]

       很多人觉得计算机很聪明,实际上,它是十分天真的。我们现在所看到它实现的强大功能,其实就是通过有限次的计算来实现的。我们说计算机很傻,是因为我们告诉它什么,它就做什么,因此,我们必须要好好和它沟通,它才能更好地为我们服务。
0.1  几个重要的概念
0.1.1  数字计算&运算符&表达式
       这个我相信大家都明白,数字计算是计算机最基本的功能,游戏里面的F1已经说得很详细了,在这里我只想说一些大家容易忽略的。
赋值运算符“=”:
       虽然很不起眼,但是,我们要注意的是,一定要把它和数学上的等号'='区分开,在这里赋值运算符的作用是把它右边的值赋给左边,左边通常是一个变量(它的概念我们即将会讲到)。不能给常量或者伪变量再次赋值。例如不能写3=2这样的式子。另外,赋值运算符的优先级是最低的一个,因此一般把所有表达式都计算完毕后再进行赋值。
除法'/',取余数'%':
       这两个运算符的用处十分广泛。大家一定要弄清整数的除法。在绝大多数编程语言中,整数的除法不会发生除不尽的问题,得到的结果,其实是两个数的商值。例如7/2应该得到3,我们可以把它理解成7 = 2 * 3 + 1因此结果是3,而不是3.5。取余数也就是7%3=1。我们要注意,余数的正负除数的相同(或者0),绝对值比除数小,因此,(-7)/3=-3,(-7)%3=2。这要提醒大家注意的是,不要随便交换乘除法的顺序,否则会造成一些不可预料的错误。
       例如,1/3*3和1*3/3最后算出的结果是不一样的,如果到这里你没有发现它们之间的不同,请回顾一下除法的意义。
       另外,如果想得到小数形式的商,就要用7.0 / 2,这样得到的是浮点数类型的3.5。注意,在计算机中,所有的浮点数都是不准确的,也就是说会有浮点误差,这是计算机精度有限造成的,因此,不能比较两个浮点数是否相等,因为通常你得到的都是“伪值”(即二者不相等)。所以,大家一定要充分利用整数,除非无法避免,尽量不要使用浮点数。
条件表达式'? :':
       具体的使用方法是:表达式1 ? 表达式2 : 表达式3
       意思就是系统先算表达式1,如果表达式1成立,则计算表达式2,否则计算表达式3。并且整个条件表达式的值就是表达式2或表达式3的值(取决于表达式1是否成立)。
这个语句因为比if语句简单,所以用途十分广泛,大家一定要熟练掌握。
       例如,max = x > y ? x : y,这里的意思就是先比较x和y的大小,如果x比y大,则再计算表达式2(也就是x),否则计算表达式3(也就是y),再把计算后的表达式赋值给变量max。
       注:条件表达式有短路原则,如果表达式1成立,那么计算表达式2,而不去考虑表达式3(此时如果计算表达式3甚至可能发生错误)。同理,如果表达式1不成立,那么计算机也不会考虑表达式2的值。
0.1.2  变量
       其实在第一部分已经用到了变量的概念,不过我相信大家对变量或多或少都有个了解,因此就没有再次引入变量的概念。但是,近期在论坛发现有一位朋友在讨论区提出了自己对变量的新看法,我跑去看了看,觉得还是比较深刻的,因此我在这里还要把它拿出来。
       首先,变量是什么?一种很粗浅的理解就是,变量就是随着程序进行而有能力发生改变的量。它改变与否当然是服从编程者的意愿。但是,这个理解在我看来,并不是很深刻,因为很多人会把类似于a,b,这样的东西叫做变量,甚至有些人根据变量名称的汉语意思,来默认这个变量的作用。其实不然,类似a,b,x,level等等,我们应该把它看作一个符号,看作我们和计算机沟通的语言,而不是变量本身,而变量本身,则是存储在计算机内存中的一块数据。而就像我看的那篇帖子中说的,变量名其实就是告诉你,在内存的某处,存储这一堆数据,而这些数据代表的值是什么。因此,x = 3的意思就是,内存中存储着一段表示整数3的数据,而x,就是所谓的“标签”,我把它换成小猫小狗什么的完全可以。
       学过C语言的朋友可能会发现,Ruby里面是没有指针的,这个机制其实为写程序的人提供了很大的方便。Ruby并不是真的没有指针,而是在使用的时候,指针和变量的区别变得模糊起来。下面我们来重点说说Ruby的变量机制。
       Ruby中,任意的符号都可以看作一个变量,并且不加声明就可以直接引用,未经过初始化的全局变量和实变量的值为nil。这个nil到底是何方神圣?其实nil是个伪变量,表示的是“无”,属于Ruby的一种抽象数据实例。程序运行前,在内存的某处的一堆数据来表示nil,所有没有被初始化的全局变量和实变量,都代表它。从这个角度来讲,Ruby中的变量和指针似乎是等价的。但是有时候会发生很多费解的事情,我们来看看下面的两个例子。
(1)
RUBY 代码复制
  1. a = 2
  2. b = a
  3. a = 3
  4. print b #print 是系统内部的输出函数

(2)
RUBY 代码复制
  1. a = [1,2,3]
  2. b = a
  3. a[2] = 4 #这是给数组第2号单元赋值为4
  4. print b

       在第一个例子里面,屏幕上将会打印2,在第二个例子里面,屏幕上会打印124。
       细心的朋友会发现,在(1)中改变了a的值,但是b的值没变;但是在(2)中就不同了,对a进行的某种操作也会在b那里反映出来。但是,无论是哪种情况,在执行b=a之后,a和b表示的是同一块数据(指向内存中的同一片区域,即地址),而不是相同数据的不同拷贝,或者说,b是a的一个别名,你要找这片内存区域,说a也行,说b也行。
       那么,我们应该如何去理解“变量”?在这里我们应该把“变量”都理解成“引用”,它们代表的并不是该数据内容的本身,而是该数据所在的内存地址。把变量的重新赋值理解为指针的指向改变,而数据内容的本身是没有变化的。(这点对Integer之类的东西貌似也是对的,因此你不能说把某一片用于表示“1”的内存区域修改,使其表示“2”,你只能把变量指针的指向从“指向表示'1'的内存区域”改变成“指向表示'2'的内存区域”)
       在后面定义函数的时候,也会发生类似的现象。写过程序的朋友知道,函数上面的参数(我们叫做形式参数,简称形参)和实际的变量(我们叫实际参数,简称实参)没有什么关系,对形参的改变丝毫不影响实参的变化。举个例子来说,假如有下面的程序:
RUBY 代码复制
  1. def swap(a,b)
  2.   t = a
  3.   a = b
  4.   b = t
  5. end

函数的作用是交换a和b两个变量的值,但是如果运行下面的程序:
RUBY 代码复制
  1. a = 3
  2. b = 4
  3. swap(a,b)
  4. p a,b

       我们会发现a和b的值并没有发生交换,原因就是计算机只是把实际参数的值拷贝给了形式参数,之后函数内部对形式参数进行的操作与实际参数无关。
       但是,我们刚才说过,变量实际表示的就是地址,而我们知道,相同的地址必定指向相同的内存空间,对同一块内存空间进行操作,变量的值当然会发生改变。例如:
RUBY 代码复制
  1. def f(a)
  2.   a[1] = 3
  3. end
  4. a = [2,2,3]
  5. f(a)
  6. print a

       得到输出的结果应该是233,这就意味着函数真正地对a进行了操作。因此,我们得出结论,传递到方法中的参数(实际是地址)会被复制一份,不会对实际的参数发生改变,而可以按照这个地址参数对其他区域进行操作,总之,实际参数的地址是不会变的。总之,一句话,这里函数参数的传值方式为“值传递(Pass By Value)”,记住这点也就不难理解上面的现象了。

最后我们要说Ruby中最常见的三种变量,这三种变量起作用的时机不同,用法不同,因此要分情况进行使用。
(1)全局变量
       在Ruby中,全局变量以$开头,例如$t,$game_party,等等。它们是在程序的任何地方都有效的变量,也就是说,如果变量名字相同,那么必定就是同一个全局变量。因此,只有我们要创建共享范围比较大(在程序的不同地方都要用)的变量时,才能用到它,否则一般不用全局变量。最常见的例子,就是跨类进行全局变量调用,如果你定义了一个类,使用的过程中需要调用别的类的内容,就要用全局变量来帮忙。例如在Window_Item类中就调用了Game_Party类的实例$game_party,试想,如果$game_party不是全局变量,在Window_Item中,计算机不认识Game_Party中的符号,那么当然会发生访问错误。这个地方,到了我说类(class)的时候,大家会有更加清楚的认识。
(2)实变量
       在Ruby中,实变量以@开头,通常是跟具体对象关联的。
例如:
RUBY 代码复制
  1. class Person
  2.   def initialize
  3.     @name = "XX"
  4.   end
  5.   def pr_name
  6.     print @name
  7.   end
  8. end
  9. print @name

       在这里,@name进入到了函数pr_name的内部,对@name进行访问时,访问的应当是“这个对象的@name”。但是如果在这个类之外写print @name,那么你一定看到的是nil,因为这时@name已经不是Person中的@name。
(3)局部变量
       在Ruby中,局部变量就是没有前缀的变量,比如level,x,等等。这一类的作用范围更窄,只是在定义函数内部有效,作为块参数的局部变量只在当前块内有效。在函数外面则是无效的。因此,我们在函数临时需要一个变量,函数结束后完全不需要的时候,就应该用这种变量。另外,函数的形式参数也要用局部变量表示。在这里面说明的一点就是,这种类型的变量是没有默认值的。例如:
RUBY 代码复制
  1. def fun
  2.   a = 2
  3.   return 2 * a
  4. end
  5. def fun2
  6.   b = a + 1
  7.   return b
  8. end

       在这里fun中的a和fun2中的a一毛钱关系都没有,它们是两个符号。所以我们明显看到,fun2中,对a进行的操作时非法的,因为此时a没有初始化,所以系统不能把它看做一个变量,在后面我们要说到,函数名字(方法)也是采用这种无前缀符号形式表示,系统会寻找与之同名的方法,如果还找不到方法,那么运行的时候,系统会提示错误信息No method error。
       但是,自动变量的好处就是用完能及时回收,保证内存空间,但是对于全局变量,如果创建出来,在写程序的人没有下达命令的时候,系统是不敢轻易回收它的。试想如果程序里面的变量都是全局的,那么用不了多长时间,内存就塞满了,这对运行程序来说是非常致命的。因此大家务必要清楚什么时候该用什么变量,才能做一个合格的“准脚本党”。
0.1.3  常量和伪变量
       在这里顺便介绍一下常量和伪变量。
   
       常量,顾名思义就是这个量代表一个特定的值,一般不能被改变。Ruby中常量的表示方法是用首字母大写的标识符表示,例如Icon,MAXNUM等等,因此常量必须要赋予初始值。当然,常量也有作用的范围,使用常量时,建议把它们放到命名空间中去,这样能够突出常量的作用域,避免发生混淆。常量的作用是为了编写程序的方便,例如,论坛上有很多这样的脚本,显示一个窗口,但是在40号开关打开的情况下,窗口是不显示的。我们可以在外面利用常量WINDOW = 40来表示控制其是否显示的开关ID,这样如果要改动,只需要改动一处即可。
       伪变量,是一类特殊的变量。Ruby中的伪变量常见的有4个,分别是self,true,false,nil。下面我们分别来说一下。
       self:被处理对象的本身,这个概念在我们讲到类的地方会详细说明,初学者会比较难懂(说实话我在接触Ruby初期就完全不懂得self的含义)
       true/false:表示一种逻辑值,实际是TrueClass/FalseClass的唯一实例。true是恒真,false是恒假,一般作为if的条件判断来使用,之间的运算符合逻辑运算。
       nil:Ruby中的特殊数据类型实例,表示“无”,注意,它并不能代替不同类所谓“空”的概念。在数组类型中,空数组用[]表示,不用nil,在字符串中,空字符串用""表示,也不用nil。nil本身没有多少方法,大家可以认为它也表示一个恒假的值。

       值得一说的是,表示伪的方式有很多,大家一定要记住2种,false,nil,这两个都表示伪,和C语言不同的是,数字0以及其他的值都表示真。[此处感谢无脑之人的宝贵意见]

0.2  几个建议
       在本章节的最后,给大家提几个建议。

0.2.1  写任何脚本都要有良好的书写规范,脚本中要注意缩进,要有层次感。变量和运算符之间,最好用空格隔开。例如x = a + b这样。
0.2.2  变量名字要取得适当,尽量取一些有含义的名字,这样能让写程序和看程序的人知道这个变量代表什么。例如表示等级,就用level,而不是用简单的m或者n。循环变量一般都用i,j表示,这点大家养成习惯就好。另外,短下划线“_”看做一个字母,如果要分隔变量之间的单词,请用_,例如icon_size,中间的“_”当然不能换成空格。大家使用标识符时,不要使用中文字符,以免发生错误。
0.2.3  要培养自主纠错能力,不要提示个什么错误就茫然不知所措。系统弹出那个小小的对话框经常会包含重要的错误信息,这样能协助你改正错误。出现的错误,可能是Syntax Error(语法错误,可能是少打end或者是捏造了不存在的写法),可能是No Method Error(未定义方法错误,可能是对nil调用方法或者是类的概念模糊),可能是Name Error(命名错误),一切都要具体情况具体分析。

*0.3  有关变量/指针/地址的重要补充(第二版更新)
       这个是补充的内容,有兴趣的初学者可以看看,当然大神什么的就免了。

       写这里的原因是时隔半年,突然发现自己的教程里面有很多东西没有说清楚。当然半年前还没有学过Java基础课,对有些东西的猜测也不敢随便写上去,现在把它补上。

       在变量机制方面,Ruby和Java非常相似。不同的是,Ruby使用变量前无需声明,因此不必告诉编译器各种变量的类型,Java中使用变量之前还是要声明的。还有一点不同就是Ruby是“万物皆对象”,即所有的数据都是一个对象,而Java除了对象以外,还有类似于int,char等基本数据类型。我们下面说的就是对象这一方面。

       如果想要创建一个对象的实体,就必须对类调用new方法,这样系统会在内存中动态地开辟一块区域,然后用调用构造方法initialize,最后把对象的引用返回给变量。如果不调用new方法,系统不会在内存中开辟区域。
下面是一个例子:
RUBY 代码复制
  1. class A
  2.   attr_accessor :x
  3.   attr_accessor :y
  4.   attr_accessor :z
  5.   def initialize(x,y,z)
  6.     @x = x
  7.     @y = y
  8.     @z = z
  9.   end
  10. end

执行:a = A.new(0,0,0),效果如图

执行:b = A.new(1,1,1),效果如图

注意到变量a和b的地址不同
执行:b = a,效果如图

       执行完毕后,a和b变成了一个地址,指向了同一片内存区域。这就相当于为同一片内存区域建立了两个标签,或者说起了个别名,无论对a还是对b访问都会访问到这个区域。也就是说,a.x = 2和b.x = 2的效果完全相同。

       那么,原来b代表的内存空间去哪了?即上面图的最右的部分,它不被任何指针变量所拥有,也不可能通过其他变量访问到它。像这种不被任何指针变量所拥有的内存区域,就应当被回收,以供别的变量使用。Ruby和Java相同,都有一套GC机制(Garbage Collection),它会定期检查内存空间是否已经不被任何一个指针变量所引用,如果没有任何指针指向它,GC就会把它回收再利用。注意,GC执行不是实时的,否则的话效率会变得很低。

       说这里有什么用呢?是为了解释RGSS1中一处不太好理解的脚本(至少个人认为是这样),要解释这个地方,还需要了解一下Ruby存储对象的机制。
我们先看下这个脚本:
RUBY 代码复制
  1. class Game_Party
  2.   #--------------------------------------------------------------------------
  3.   # ● 同伴成员的还原
  4.   #--------------------------------------------------------------------------
  5.   def refresh
  6.     # 游戏数据载入后角色对像直接从 $game_actors
  7.     # 分离。
  8.     # 回避由于载入造成的角色再设置的问题。
  9.     new_actors = []
  10.     for i in [email]0...@actors.size[/email]
  11.       if $data_actors[@actors[i].id] != nil
  12.         new_actors.push($game_actors[@actors[i].id])
  13.       end
  14.     end
  15.     @actors = new_actors
  16.   end
  17. end

       这个方法出现在Game_Party中,我第一次看这里的时候,注释就没有看懂。什么叫“游戏数据载入后角色对象直接从$game_actors中分离,回避由于载入造成的角色再设置问题”?

       而后全局搜索$game_party.refresh,发现它只出现在一个地方:Scene_Load(101)。可见真的是为了处理载入方面的问题。那么,既然载入了,为什么还要多次一举这样设置一番呢?

       稍微懂点RGSS1的人都知道,$game_actors是存储所有主角角色的变量,编写的时候利用了类Array的外壳。而$game_party.actors是存储当前队伍中所有角色的数组,当然这个集合是所有角色集合的子集。那么,这两个地方都有指向Game_Actor类的引用,它们必须要保持一致。下面的图形能够说明这一问题。

       但是,如果涉及到对象的存储问题,实际就不是这样了。我们在Scene_Save中可以看到,系统对$game_actors和$game_party都做了存储。因为写入文件的是对象,所以不但要把对象本身写入,而且要把对象内部的引用也要写入,如果对象内部的引用还有引用,那么也要写入……说白了,要写入$game_party本身,也要写入$game_party.actors这个数组对象,注意,写入$game_party本身只写入了$game_party.actors数组的引用,数组本身的内容没有写入。由于$game_party.actors是对象数组,因此还要把数组中的每一个Game_Actor对象都写入进去。而刚才我们提到,$game_actors中也含有所有Game_Actor对象,这样一来,同样的数据要写入两遍。读取的时候自然也要读两遍。我们在这里做个测试:
RUBY 代码复制
  1. class Scene_Load
  2.   #--------------------------------------------------------------------------
  3.   # ● 读取存档数据
  4.   #     file : 读取用文件对像 (已经打开)
  5.   #--------------------------------------------------------------------------
  6.   def read_save_data(file)
  7.     # 读取描绘存档文件用的角色数据
  8.     characters = Marshal.load(file)
  9.     # 读取测量游戏时间用画面计数
  10.     Graphics.frame_count = Marshal.load(file)
  11.     # 读取各种游戏对像
  12.     $game_system        = Marshal.load(file)
  13.     $game_switches      = Marshal.load(file)
  14.     $game_variables     = Marshal.load(file)
  15.     $game_self_switches = Marshal.load(file)
  16.     $game_screen        = Marshal.load(file)
  17.     $game_actors        = Marshal.load(file)
  18.     $game_party         = Marshal.load(file)
  19.     $game_troop         = Marshal.load(file)
  20.     $game_map           = Marshal.load(file)
  21.     $game_player        = Marshal.load(file)
  22.     # 加上这个
  23.     p $game_actors[1].to_s,$game_party.actors[0].to_s
  24.     # 魔法编号与保存时有差异的情况下
  25.     # (加入编辑器的编辑过的数据)
  26.     if $game_system.magic_number != $data_system.magic_number
  27.       # 重新装载地图
  28.       $game_map.setup($game_map.map_id)
  29.       $game_player.center($game_player.x, $game_player.y)
  30.     end
  31.     # 刷新同伴成员
  32.     $game_party.refresh
  33.   end
  34. end

在读取数据完毕之后,我们来看看这两个对象的地址。注:所有对象都有to_s方法,如果不加重设的话,返回的是引用的地址。

       由于$game_actors实际有意义的数据是从下标[1]开始,因此$game_actors[1]和$game_party.actors[0]是一样的,但是它们的地址却不一样。这就要调用$game_party.refresh,让这两部分引用保持一致性。
       大家可以想想,如果不调用$game_party.refresh,会出现什么后果,而此时的内存地址模型又是怎样的?

       预备知识就说到这里吧,虽然比较繁琐,但是我觉得在帮助上还是看不到的。这个帖子是连载,有时间我会写后面的内容,那些才是重头戏,大家一起期待之后的帖子吧。

点评

编辑帖子时就会有这个提示。说明楼主更新了  发表于 2014-7-5 18:54
每次都看见你在“和我同一个楼层”评论,但是每次我都找不到  发表于 2014-7-5 18:54
短消息的内容我已经放在32L,请务必看一看  发表于 2014-7-2 15:48
请查收短消息  发表于 2014-7-2 15:34
回复:无脑之人 是这样的哈,我下了Ruby的源码,发现是从C上移植过来的,因此也没多想,多谢提醒。  发表于 2013-10-13 20:46

评分

参与人数 8星屑 +238 收起 理由
gonglinyuan + 30 精品文章
zmz6668 + 8 精品文章
季长风 + 10 精品文章
jspython + 10 精品文章
秋寒 + 30 塞糖
wingzeroplus + 20 精品文章
feizhaodan + 120 奖赏条例
无脑之人 + 10 精品文章

查看全部评分

Lv1.梦旅人

梦石
0
星屑
365
在线时间
4 小时
注册时间
2012-10-27
帖子
1
2
发表于 2013-10-13 10:13:41 | 只看该作者
受无脑之人推荐而来……

点评

大家共同进步哈  发表于 2013-10-13 10:52
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
2012
在线时间
1018 小时
注册时间
2011-6-19
帖子
633

极短24参与开拓者

3
发表于 2013-10-13 10:17:40 | 只看该作者
关注rmxp的教程,正好打算开始学呢......
楼主可以布置点作业什么的吧(≧∇≦)

点评

布置作业这个事情我还真没考虑过,不过你可以把你练习的脚本拿来看看,把困惑问问什么的。  发表于 2013-10-13 10:53
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
60
在线时间
568 小时
注册时间
2012-9-7
帖子
611
4
发表于 2013-10-13 10:45:34 | 只看该作者
原来数组居然不会被计算机复制一份,今天才知道,受教了(虽然怎么用过数组)

点评

要想复制一个数组,请用b = a.clone,这里a是一个做好的数组。  发表于 2013-10-13 10:55
FTM正式版已经发布,点击图片开启传送门
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
76
在线时间
1379 小时
注册时间
2012-7-5
帖子
1698

开拓者

5
发表于 2013-10-13 12:57:57 | 只看该作者

标题

本帖最后由 kuerlulu 于 2013-10-13 16:39 编辑

好顶赞!本来我也想写的 看lz发了我就来看看
赋值符号=是让左右相同,不一定是右边给左边,只要其中一个是未定义的变量就行了,如果两边都已经赋值,那么会引发错误。
补充:是我没说好,我的意思是a = 1和1 = a等价,已经赋值的情况下。。
a=1;b=2;a=b;print a.to_s
把上面这行测试看看。。爪机无力验证了
变量起名嘛。。用中文也是完全可以的,只是中文夹在英文中间会不美观而已。。

点评

那么在下也说最后一次,一般而言的赋值操作,一定是将右边的值给左边,右边必须被定义,左边无所谓  发表于 2013-10-13 18:51
不要误导新手。比如:a = 1 a已经赋值,那么再 a = 10 ,那也一样哈,不会引发什么错误。  发表于 2013-10-13 16:09
不是右边给左边难道还能是左边给右边?  发表于 2013-10-13 14:18
囧,我还没见过站上有用中文做变量名的,果然是孤陋寡闻了吗  发表于 2013-10-13 13:01

  -fk: -azogi:
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
96 小时
注册时间
2013-9-21
帖子
112
6
发表于 2013-10-13 17:02:15 | 只看该作者
那如果想把一个数组copy一份,有什么简单的办法?
我的办法是先建个新数组,然后用循环把原数组的内容一个一个加进去……

点评

数组名可以看做数组首地址 所以b=a只是把b和a指向了同一个数组 对a的修改会影响到b 所以你那循环式正确的思路 简单就clone 估计函数做的事情也是循环  发表于 2013-10-14 14:39
最容易理解的办法:a = [1, 2, 3] ; b = a ; p b  发表于 2013-10-13 21:04
在4楼我已经说了,复制一个数组要用b = a.clone  发表于 2013-10-13 18:36
回复 支持 反对

使用道具 举报

Lv4.逐梦者 (版主)

梦石
0
星屑
9532
在线时间
5073 小时
注册时间
2013-6-21
帖子
3580

开拓者贵宾剧作品鉴家

7
 楼主| 发表于 2013-10-13 20:56:52 | 只看该作者
本帖最后由 RyanBern 于 2015-7-31 10:26 编辑

[box=RoyalBlue]
第1章节:类
[/box]

       有了预备知识,我们就可以进入Ruby中运用最广泛的部分——类(class)了。我在这里要说的是,在介绍类之前,我认为大家应该对Ruby的语法有了大致了解,对一些基本的概念也都掌握了,例如控制语句,函数,所以就没有再重复这些东西。如果大家有不明白的,请关注下RMXP的F1,那里面已经说得很清楚了。

1.1  类&实变量&实例方法
1.1.1  类的概念
       类其实是一个比较难说的概念,Ruby是面向对象的编程语言,所谓对象,就是计算机中的抽象数据,而且数据之间有一定的逻辑和结构关系。所谓类,不严格地来讲,就是抽象数据类型。翻开RMXP的F1,我们会发现,所有的类,竟然也都是一个对象,这就是我们所说的“万物皆对象”的观点。打比方来说,人类可以看作是一个类,那么人类的每一个个体就可以看作是一个对象,不知道这样是不是能好理解些。从感性的角度上来说,类应当是对象的一个集合。所有的人就构成了一个人类,而人是人类的一个实例(Instance)。类的上面定义了“属性(Property)”和“方法(Method)”。属性我们可以理解为描述(类生成)对象的性质,比方说人的身高,体重等等;方法可以理解为对象的某种操作,比方说吃饭,喝水,说话等等。对于类本身,还有专属于类的类变量和类方法。另外,类之间有继承关系,有多态性(Polymorphism)。如果你能理解这些,那么理解类也就不难了。
1.1.2  实变量&实例方法
       每一个类的对象(实例)都有属于自己的变量和方法,所谓实例的变量,就是定义在该对象中的,只限于该对象自己使用的变量,实例方法,也就是只有属于这个类的对象才能使用的方法。我们举了例子来具体说明:
RUBY 代码复制
  1. class Person #定义一个叫Person的类
  2.   def initialize
  3.     @name = "Ryan"
  4.     @gender = 'M'
  5.   end
  6.   def hello
  7.     print "I am " + @name + "!"
  8.   end
  9. end

       这个名叫Person的类就定义好了。
       首先说一下,Person是上面我们所说那个“类的类(Class)”中的一个实例,Class类中的实例只有一个方法叫做new,调用方法要用圆点运算符“.”表示。格式是a.method(…),
       其中a是该类的一个实例,method是方法的名字(函数名字,必要的时候要带参数)。
     【关于initialize方法】每个类都能定义一个名叫initialize的方法,这个方法比较特殊,是类的构造方法,或者成为“构造器”。当你使用Xxx.new生成一个类的实例时,便自动调用了这个initialize方法,'.new'后面跟的参数会原封不动地传递到initialize中去。
       刚才我们说过了,Person是Class中的一个实例,那么Person.new的意思就是生成一个Person类的实例。我们接下来插入下面一个代码:
RUBY 代码复制
  1. ryan = Person.new
  2. ryan.hello

       这两句的意思是生成一个Person类的实例,储存在一个变量中,然后调用该类的方法。因此执行完这两句之后,屏幕上将会打印出I am Ryan!的字符串。
       在上一章节我们已经说过,如果一个变量是对象的一部分数据,那么要把它定义成实变量的形式(即@开头的变量),这样定义出来的变量在整个类的内部都是有效的。试想如果把上面的语句中所有的@都去掉,那么后果将会是……

给initialize方法添加参数

       我们回头再来看看这个程序片段。
RUBY 代码复制
  1. class Person #定义一个叫Person的类
  2.   def initialize
  3.     @name = "Ryan"
  4.     @gender = 'M'
  5.   end
  6.   def hello
  7.     print "I am " + @name + "!"
  8.   end
  9. end

       我们会发现,这个类别建立好了之后,所有的实变量的值都是固定的,这就导致我们只能建立相同内容的实例,因此我们要稍稍改装一下,让建立的实例拥有不同的初值。
RUBY 代码复制
  1. class Person #定义一个叫Person的类
  2.   def initialize(name,gender)
  3.     @name = name
  4.     @gender = gender
  5.   end
  6.   def hello
  7.     print "I am " + @name + "!"
  8.   end
  9. end

       这里的initialize是所有类一般要定义的方法,在一些面向对象的语言中,把这个方法叫做“构造方法”或者“构造器”。实际上,Person.new这个语句已经在调用initialize,所有类的初始化函数名字必须是initialize,这也是Ruby的规定。这里的initialize带有2个参数,我们回忆一下函数参数的概念,就会发现@name和name根本不是一回事。这样定义好了之后,我们可以写下面的语句了。
RUBY 代码复制
  1. ryan = Person.new("Ryan",'M')
  2. ryan.hello

       这样和刚才的效果相同,但是我们可以随便设置实例的初值。

实变量公开化

       虽然我们可以用上面定义的类生成一个个实例,但是我们想一下这种情况:有一天Ryan出门了,见到了一个漂亮的女孩,因为我们定义了hello方法,所以Ryan很轻松地向她打了个招呼。女孩于是问:"Ryan"这个名字怎么拼啊?Ryan这时却抓耳挠腮,因为Ryan还没学会怎么让对方知道自己的名字拼写。再比如,Ryan觉得这个名字太简单了,想改成"RyanBern"(依然很矬),但是依然没有办法改变自己的名字。解决上面两个问题,就要用我们的实变量公开化。
RUBY 代码复制
  1. class Person #定义一个叫Person的类
  2.   def initialize(name,gender)
  3.     @name = name
  4.     @gender = gender
  5.   end
  6.   def hello
  7.     print "I am " + @name + "!"
  8.   end
  9.   ####
  10.   def name
  11.     return @name
  12.   end
  13.   def name=(name)
  14.     @name = name
  15.   end
  16.   ####
  17. end

       我们加入了两个方法,第一个函数是返回实例中@name的值,第二个函数的作用恰恰是修改实例中@name的值。
       我们注意到,这两个方法的名称比较奇特,和@name这个实变量很相似。实际上,第一个方法叫做'name',第二个方法叫做'name=',并且带一个参数。你可能会问了,怎么会有名字这么奇怪的方法?这种方法在C++/Java中都是没有的(除非你想操作符重载,但是即使重载了,效果也不如Ruby里面的这个好),而Ruby之所以这样做,是出于对实变量保护隐藏的目的。我们在类对象的外面,是没有办法修改对象内部的变量的,如果真的需要修改,那么只能通过定义方法的形式。注意,第二个函数开头的两个name是不一样的,前面那个是方法名字,括号里面的是形参名字。
       有了这两个函数,我们可以写(假设Ryan已经定义好了):
RUBY 代码复制
  1. nam = ryan.name #将ryan的@name赋值给变量nam
  2. ryan.name += "Bern" #将ryan的@name后面加个"Bern"


       这里注意,上面的例子中第二句同时调用了'name'方法和'name='方法,请仔细思考一下。

       这种通过定义方法来公开变量,是Ruby中最常见的。实际上,定义这样的函数之后,可以把被定义的变量看作是类的一个属性,我们可以获取属性的值或者修改属性的值(但严格来讲,【属性】这个概念并不存在于Ruby当中)。一种简单的理解,就是把'name','name=','@name'联系在一起,'name'方法返回'@name'的值,'name='方法修改'@name'的值。将原点运算符'.'理解为“的”,那么a.name就可以理解为某人的名字。但是我们要知道,其实圆点运算符表示的就是类方法的调用,加上属性的概念是为了突出这种方法的特殊地位。这样,相关的实变量便可以通过外部访问。

        当然,这样编写可能会浪费大量的篇幅,我们在这里有比较简单的形式来代替上面的两个方法。
RUBY 代码复制
  1. attr_accessor :name
  2. attr_reader :name
  3. attr_writer :name

       其中,attr_accessor是后面两个的合并,同时具备两种功能。
       而attr_reader只定义了'name'方法;attr_writer只定义了'name='方法,表示只允许读或者只允许写。

       在这里我们注意一个问题,'attr_'开头的这一串文字,不是所谓关键字,也不是所谓变量声明。它们本身就是方法(隶属于Module),而它们的作用,就是生成其他的方法。而':name'就是它们的参数(这是一个Symbol对象,也可用字符串对象代替)。它们生成的方法有最简单的形式,即'name'方法是单纯返回@name的值,'name='方法是单纯修改@name的值。如果你想要让这两个方法变得复杂,那么就不要使用'attr_xxxx'。

       因此,我们打开脚本编辑器,映入眼帘的Game_Temp,里面就是这些东西,仔细看看也没什么了不起的是吧?

1.1.3  父类&子类&类扩充&方法重写(Override)
       在这里我们引出父类和子类的概念。假设我们定义好了一个类Person,现在我定义它的子类Student,那么我们要写:
class Student < Person,表示Student是Person类的子类。那么Student这个类所有的实例的性质,都会具有Person类的所有性质。即如果student是Student的一个实例,那么在Person类中定义的实变量和方法,student都能继承下来。这有点类似于“遗传”的概念。因此我们可以写student.name,student.hello等等这样的语句。
       引入这一概念的原因,也是为了编程的方便。在计算机所有的对象中,虽然很多对象都属于不同的类,但是他们也同时具有很多的共同性质。那么我们可以先把它们的共同性质定义出来,做成一个父类,然后再分别定义它们特有的性质。在RGSS中,最明显的就是Window类,窗口类的父类是Window,然后Window_Base是它的一个字类,表示一般的窗口;Window_Selectable则是Window_Base的一个字类,表示具有光标的滚动窗口类。这两个类会衍生出一系列的子类,总体说分两种,不带光标以及滚动功能的,基本都是Window_Base的子类,具有光标以及滚动功能的,基本都是Window_Selectable的子类。有人会说,既然Window_Selectable是Window_Base的子类,那么我们岂不是可以把下面所有窗口类都归到Window_Selectable下?这是不行的,Window_Selectable比Window_Base更丰富,但是需要的内存空间也就更多,如果你要一个不带滚动功能的类,却设置到了Window_Selectable下,那么很多内存空间都没有被利用上,这显然是我们不需要的。

第二版新增
       值得一提的是,类之间的结构定义体现了一个人编程素养的高低。在制作程序时,如果要新增某种功能,一定要引入新的类。但是怎么去引入?是直接引入所需类,还是从父类做起,以便有更好的适应性?这都是问题。对于两个不同的类,是做成父类——子类比较好,还是做成两个平行毫不相关的类比较好?答案并非十分明确。在这里举一个Java中的例子,我觉得很有参考价值。在Java的AWT包中,有Menu(菜单类)和MenuItem(菜单项类,即菜单拉开中的各个选项)。如果让你制作,你会把它们的关系定义为:

(1)父类——子类?

(2)子类——父类?

(3)两个平行类?

       在Java中,这两个类的关系是第二种,Menu(菜单类)是MenuItem(菜单项类)的子类?啥?搞错了吧?菜单项包含在菜单的里面,怎么反而菜单项是父类?这个问题稍加思考,便可以赞叹编写Java-AWT包人员的高明之处。我们点开一个菜单,里面有各种菜单项,当然也包括一些可展开的菜单(即二级菜单),从这个角度上来讲,菜单就是一种特殊的菜单项,只不过它可以展开而已。因此MenuItem是Menu的父类是没问题的。

       提到了父类——子类,那么还有一个东西就不得不提,那就是抽象类(abstract class)

       尽管在Ruby中,并没有看到类似于抽象类的字眼,但是我们还是能够深刻地体会这种编程思想。面向抽象的编程思想也是编写这样程序的重要思想之一。抽象类是高度概括的一个类,它包含着它所有子类的一般行为。正因为它的高度概括性,它就必须作为一个父类出现,等待着别的类去继承它,扩充它。而自己本身,由于内容过于概括,因此不适合实例化一个对象。你问我这种现象的例子有没有?很明显,Object类就是一个抽象类。它定义了对象的一般行为,Object类作为所有类的祖宗,有着无可替代的高度概括性。定义出一个好的抽象类能够让你的代码更加简洁,易懂,而且显得有水平。

       我们再看一个例子,RGSS1中的Game_Battler,它就是一个抽象类。它的两个已知子类分别是Game_Actor和Game_Enemy。啥?为啥说它是抽象类?我们看看下面的代码。
RUBY 代码复制
  1. class Game_Battler
  2.   #--------------------------------------------------------------------------
  3.   # ● 获取 MaxHP
  4.   #--------------------------------------------------------------------------
  5.   def maxhp
  6.     n = [[base_maxhp + @maxhp_plus, 1].max, 999999].min
  7.     for i in @states
  8.       n *= $data_states[i].maxhp_rate / 100.0
  9.     end
  10.     n = [[Integer(n), 1].max, 999999].min
  11.     return n
  12.   end
  13. end

       这是定义MaxHp的一段代码,比较简洁。我们注意def下面那句n = base_maxhp + @maxhp_plus ....

       问,base_maxhp为何物?

       它并不是局部变量,那就是一个方法了。

       在Game_Battler搜索def base_maxhp,结果啥也没找到。

       也就是说,Game_Battler里面的maxhp引用了一个没有在类内部定义的一个方法?这怎么可以?

       这怎么不可以??

       我们再次搜索def base_maxhp,结果在Game_Actor和Game_Enemy中均找到了它的定义。原来,这个方法不在父类中定义,而是在子类中定义。父类的方法却要用到子类的方法,这不是差辈儿了么?不是这样的,由于父类的高度概括性,导致无法在父类中描述base_maxhp具体的执行过程,而它的两个子类中,描述base_maxhp的执行过程是可以知道的。因此,父类就弄出这么个方法放到这儿,表示我不在这里定义,而是在子类中进行定义。这样只是起说明作用却没有实体的方法叫做“抽象方法”,而在Ruby中,你甚至都不用声明一个抽象方法。
       那允许我再问个问题,如果对Game_Battler类的一个对象调用maxhp方法,则又如何?岂不是要出现No method error?
       要记住,既然是抽象类,一般就不用它去生成一个实例,不生成实例,何谈调用一说啊?RGSS1中出现过Game_Battler.new吗?显然没有。

第二版更新·完
       当然,如果一个类的方法和属性定义不能满足我们的需求,而我们又不想再定义一个子类,这时候我们就要对原有的类进行扩充。当然,你可以在原来的类上面进行修改,不过,如果是系统整合的话,我们会采用以下的方式:
RUBY 代码复制
  1. class Person
  2.   def goodbye
  3.     print "See you!"
  4.   end
  5. end

       注意,Person我们已经在前面定义好了,但是你完全可以再写一遍class Person,表示对该类进行追加定义。

方法重写(Override)
       如果一个方法已经在父类被定义过,在子类再次对它定义(通常利用父类已有的方法)就叫做方法的重写。例如:
RUBY 代码复制
  1. class Student < Person
  2.   def hello
  3.     print "I am " + @name + ", and I am a student."
  4.   end
  5. end

这里我们重写了方法hello,那么在调用student.hello的时候,屏幕上显示的就是新定义叙述的内容(即后面多了and I am a student)。
       不过,这顶多算是把原来的方法覆盖掉了,重写的味道还不是很浓。我们在定义子类方法的时候,通常是对父类的同名方法的扩充,这就要用到关键字super,例如:
RUBY 代码复制
  1. class Student < Person
  2.   attr_accessor :student_id #定义新属性,学号
  3.   def intialize(name,gender,student_id)
  4.     super(name,gender)
  5.     @student_id = student_id
  6.   end
  7. end

       在这里,我们调用了Person类的initialize的方法,利用的是关键字super,当然参数什么的不能少。为什么不能写initialize(name,gender)呢?如果这样的话,系统会认为这是一种递归定义,即函数调用自身的过程(Ruby中允许递归定义函数),而并不是调用父类的同名方法,因此我们必须采用这样的形式。

       注意,super关键字的使用有一定特殊性,在使用super的时候,要格外注意参数问题。在这里推荐,即使是被调用的方法没有参数,也要跟一对空括号'()'来表示没有参数,而不能什么都不带直接写super。这是因为,如果super后面什么都不跟,那么默认传进去的参数和正在定义的方法相同,这有可能引起错误。例如:
RUBY 代码复制
  1. # 定义父类 A
  2. class A
  3.   def initialize
  4.     @a = 0
  5.   end
  6. end
  7. # 定义子类 B,这里 B 的 initialize 方法中,super的使用是错误的!!
  8. class B < A
  9.   def initialize(b)
  10.     super
  11.     @b = b
  12.   end
  13. end
  14. # 定义子类 C,这里 C 的 initialize 方法中,super的使用是正确的
  15. class C < A
  16.   def initialize(c)
  17.     super()
  18.     @c = c
  19.   end
  20. end

       如果有上面的定义,那么在使用B.new时,就会发生Argument Error。

方法的覆盖
       如果一个方法在一个类中被定义,再次对其定义就叫做方法的覆盖。例如
RUBY 代码复制
  1. class A
  2.   def test
  3.     p 1
  4.   end
  5. end
  6. a = A.new
  7. a.test # => 1
  8. class A
  9.   def test
  10.     p 2
  11.   end
  12. end
  13. a = A.new
  14. a.test # => 2

       在这里 A 类的方法'test'被定义了两次,那么后定义的会覆盖之前定义的。如果调用A.new.test,实际调用的方法取决于这句话的位置,如果在第一个test后第二个test前使用,那么实际调用的就是覆盖前的方法;如果在第二个test后使用,那么实际调用的就是覆盖后的方法。
       在这一小部分的最后,我想买个关子,在方法的定义和重定义中,还有一个重要的“别名”机制alias,这个东西的存在,为我们写脚本带来了更大灵活性,那么关于alias,我们要放到后面的章节进行讲解,这里就先不说了。

       应大家的要求,我们在这里布置一道小练习题。

       练习:请定义一个表示水瓶的类,水瓶有两个属性,一是最大容积(用一个正整数表示),二是当前水瓶中盛放水的量(也用一个非负整数表示,不得大于最大容积)。在这个类上面定义三个类方法:1.将一个水瓶装满水;2.清空一个水瓶里面的水;3.将这个水瓶自身里面的水倒入另一个水瓶,注意,不是随机地倒,倒完之后,保证自身是空的或者对方是满的。
       定义类class Bottle并验证你定义的方法。

第二版新增
*1.1.4  模块(module)简介
       首先,什么是模块?模块是用于实现某些特定功能的代码的组合。它与类不同,和类相比较,模块内部定义了一些常量和方法,从完整性看,模块不如类完整。不过模块内部可以定义内部类,这样定义的好处是让类的作用更加清楚。

       如何定义一个模块?
       利用关键字module可以定义一个模块。
RUBY 代码复制
  1. module Action
  2.   WORD_ON = "On"
  3.   WORD_OFF = "Off"
  4.   def turn_on
  5.      print WORD_ON
  6.   end
  7.   def turn_off
  8.      print WORD_OFF
  9.   end
  10. end

       这就是一个简单模块的定义。从这几句代码来看,定义了两个字符串常量和两个(普通)方法,而这两个方法,从名字来看是“打开”,“关闭”。可是这有什么用?我们知道,我们可以打开收音机,可以打开电脑,也可以打开煤气灶。那么,对于这几个类,都有相应的“打开”和“关闭”方法。一个个定义显然不妥,我们想到了定义父类。收音机和电脑可以归入到“家用电器(Appliance)”类中,但是煤气灶无论如何也不是什么家用电器。这可咋办?那就定义到更高的父类中,把家用电器和“煤气灶所属的类”归到一个类上去。那我们叫它“家庭用品(Utensils)”类。但是在这个类定义turn_on和turn_off方法,问题就更多了。家庭用品不但包括家用电器,煤气灶,还包括床,被单啥的,难道它们也可“打开关闭”?显然不行。那咋办?
       我们经过观察,可以发现,turn_on和turn_off只是两个特定的功能,和类什么的关系不是很大。于是我们把它定义到模块当中,然后在类中把这个模块糅合进去(Mix-in)。这样,类中便有了模块中实现的方法。

       要在类中糅合模块,要用include方法。
RUBY 代码复制
  1. class Radio < Appliance
  2.   include Action
  3.   attr_reader :brand
  4.   def initialize(brand)
  5.     @brand = brand
  6.   end
  7. end
  8. ra = Radio.new("ChangHong")
  9. ra.turn_on # => "On"
  10. ra.turn_off # => "Off"

       这样Radio对象就可以使用Action模块里面的功能。

       啥?不会用?那么我再说个简单的用法好了。Module可以当作命名空间(namespace)使用,主要是各种参数和常量的定义。啥?直接定义在外面?这可不好,万一别人定义的和你自己定义的重复了就麻烦了,还是放到命名空间里面好。我们看看著名的Fuki对话框脚本:
RUBY 代码复制
  1. module FUKI
  2.  
  3. # 头像图片保存目录的设定
  4. HEAD_PIC_DIR = "Graphics/Heads/"
  5.  
  6. # 是否显示尾部图标
  7. TAIL_SHOW = true
  8.  
  9. # Skin的设定
  10. # 使用数据库默认窗口Skin情况下这里使用[""]
  11. FUKI_SKIN_NAME = "skin3"   # 呼出对话框用Skin
  12. NAME_SKIN_NAME = "skin3"   # 角色名字窗口用Skin
  13.  
  14. # 字体大小
  15. MES_FONT_SIZE = 22    # 呼出对话框
  16. NAME_FONT_SIZE = 14   # 角色名字窗口
  17.  
  18. # 字体颜色
  19. #(设定为 Color.new(0, 0, 0, 0) 表示使用普通文字色)
  20. FUKI_COLOR = Color.new(255, 255, 255, 255)  # 呼出对话框
  21. NAME_COLOR = Color.new(255, 255, 255, 255)  # 角色名字窗口
  22.  
  23. # 窗口透明度
  24. # 如修改窗口透明度请同时修改尾部箭头图形内部的透明度
  25. FUKI_OPACITY = 255    # 呼出对话框
  26. MES_OPACITY = 255     # 默认信息窗口
  27. NAME_OPACITY = 255    # 角色名字窗口
  28.  
  29. # 角色名字窗口的相对位置
  30. NAME_SHIFT_X = 0      # 横坐标
  31. NAME_SHIFT_Y = 16     # 纵坐标
  32.  
  33. # 窗口表示时是否根据画面大小自动检查窗口出现的位置,
  34. # 自动改变位置( true / false )
  35. # 设置成 true 可能出现箭头图标颠倒的问题 <- bbschat
  36. POS_FIX = false
  37.  
  38. # 在画面最边缘表示时的稍微挪动
  39. # 使用圆形Skin的角和方框的角重合的情况下为 true
  40. CORNER_SHIFT = false
  41. SHIFT_PIXEL = 4   # true 时挪动的象素
  42.  
  43. # 角色高度尺寸
  44. CHARACTOR_HEIGHT = 48
  45. # 呼出对话框的相对位置(纵坐标)
  46. POP_SHIFT_TOP = 0         # 表示位置为上的时候
  47. POP_SHIFT_UNDER = 0       # 表示位置为下的时候
  48.  
  49. # 信息的表示速度(数字越小速度越快,0为瞬间表示)
  50. # 游戏中 可随时用数字代入 $mes_speed 来改变消息表示速度。
  51. MES_SPEED = 1
  52.  
  53. end

       Fuki脚本把这些常量都定义到module当中了,他为啥不定义在最外面?显然是考虑冲突的问题。因此我们写脚本也要有这样的习惯,把常量都定义到模块中,不要定义在外面,更不要定义成全局变量。例如(某升级提示脚本):
RUBY 代码复制
  1. $不显示升级窗口 = 88

       开始觉得这样没什么,后来越看越觉得不好。

       想要在别的类引用模块内部的常量,要用到'::'运算符。
RUBY 代码复制
  1. p FUKI::Head_PIC_DIR

       当然如果一个模块已经被include在类内,就可以在类内部直接用了。

       我又忍不住联想了,其实module这个东西吧,跟接口(Interface)比较相似,都是实现某些特殊功能的语句组合。他们还有一点不谋而合:由于Ruby中无法多重继承(实际上多重继承并不十分合理,个人认为,但是多重继承有可取之处),因此module便可以实现某种意义上的“多重继承”。这点利用接口(Interface)也可以做到。

第二版更新·完

1.2  两个重要的类
       在这里我们要介绍两个我们经常用的类。
1.2.1  数组Array
       数组可以看作是一些对象的有序集合,在内存中占据一块连续的区域。在C语言中,数组有固定长度,而且数组内包含的元素类型必须是相同的。但是在Ruby中,数组运用就灵活了很多。这里的数组不但没有固定的长度,而且内部的元素类型也不必相同。例如:[0,2,nil,[1,2]],这个数组包含4个元素,从左到右分别是两个整数,nil,还有另一个数组。对数组成员进行访问,直接用下标表示,假如a是一个数组,第0号单元就是a[0],也就是物理位置上的第一个。下标从0开始而不是从1开始,这个为处理问题提供了很大方便(我一开始也不懂为什么要从0开始,编了四年之后发现这是很方便的)。所以大家还是尽量熟悉它吧。下面我们说下数组常用方法,这个在F1中输入array搜索就能找到。
  • 初始化
    a = []或者a = Array.new
    别小看这个东西,很多时候因为少了这句话,会引发NoMethodError for nil : NilClass
  • 将一个元素x放在数组的最后面
    a.push(x)
    将数组里面添加元素的常用方法,类似于进栈操作。当然push可以跟很多参数,表示把参数依次添加到数组末尾。
  • 删除数组中最后一个元素,并返回它
    element = a.pop
    注意这个函数有两个功能,一是删除,二是取值,类似于出栈操作。
  • 删除数组中值为val的所有元素
    a.delete(val)
    注意,删除之后,所有元素依然是相邻的,下标的位置可能改变。
  • 删除数组中位置为nth的元素
    a.delete_at(nth)
    同上,删除之后其他的元素位置会移动。
  • 判断数组中是否有元素val
    include?(val)
    注意,Ruby中,以'?'结尾的函数的功能约定为判断(当然只是一个约定),返回值要么是true,要么是false。我希望大家也把这个“传统”延续到自己的脚本编写中。
  • 将数组排序
    a.sort
    a.sort!
    a.sort!{|a,b|…}
    注意,Ruby中,以'!'结尾的方法称为“重磅方法(Bang Method)”,告诉你这是一个比较危险的方法,很可能破坏原始数据。
    其中第三个为带块的排序,即按照一定标准排序。例如:a是一个数组,里面元素是我们刚才定义好的Person的实例,现在要将所有元素按照年龄大小排序,那么就要写a.sort!{|a,b| a.age – b.age}。注意,花括号里面的a和b是形式参数,意思就是取好了数组中的两个元素后,再取他们的age属性,和外面的表示数组的a无关。
  • 遍历整个数组(第二版变更)
    each{|item| ...}
    each_index{|i| ...}
    each_with_index{|item, index| ...}
    具体块中的操作自己设定。
    例如:
    RUBY 代码复制
    1. a = (1..100).to_a
    2. s = 0
    3. a.each{|n| s += n}
    4. p s # => 5050

    each方法就是按照次序取出数组中所有的元素,然后根据元素进行某种操作。
    each_index实际就是对数组的索引进行遍历,作用等同于(0...a.size).to_a.each{|i| ...}
    each_with_index每次把索引和相对应的单元都取出来,以便在块内使用。
    注意:熟悉RGSS1的人喜欢用for item in a~end的形式,其实for是一个语法糖,具体调用的方法还是each,这不过是照顾那些C++的人而设置的。
    注意:使用each迭代器时,不要使用类似于delete这样对原始数组有破坏的方法,这是因为在迭代过程中要尽量保持原有数组的不变性(这种行为在C#里面是不能通过编译的)。

1.2.2  Hash表
       Hash表又称关联数组,它相当于在集合A和集合B上建立了一个映射。也就是说,它将A中的每个元素映射到B中。A中的元素成为主键(key),B中的元素称为值(value)。每一个主键都对应唯一一个值。由于主键之间无法排序,所以Hash表也是没有顺序的。
       具体的操作大家就自行F1,输入Hash查找一下吧。这里就不多加叙述了。


       类的基础知识我们就说完了,肯定不能说的很详细,数组和Hash表的用法,大家如果想知道更多,请参考帮助文件,那说的应该比我要全。在下一章节我们要全局地分析一下RMXP默认的系统,了解一下整个程序究竟是怎样工作的,大家就敬请期待吧!
      

点评

到了RGSS3不都是用each了吗  发表于 2013-10-19 21:08
回复:无脑之人 相应概念已经改正,多谢。  发表于 2013-10-14 12:48
根本上是类作为对象而被定义的特殊方法,类变量也是一样,是@@开头属于类的,@开头的叫做实例变量  发表于 2013-10-14 12:25
突然发现一个问题,「该类的方法」与「类方法」不同,该类的方法是由该类的实例所调用的,而类方法是由类自己调用【类名.方法名】  发表于 2013-10-14 12:24
表示从本章开始有30%的内容看不太懂了  发表于 2013-10-14 09:50
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
117
在线时间
552 小时
注册时间
2012-8-18
帖子
1429
8
发表于 2013-10-13 22:09:43 | 只看该作者
交作业下面白字↓

class Bottle
  def initialize(maxvolume,volume=0)
    @maxvolume,@volume = maxvolume,volume
  end
  attr_reader :maxvolume,:volume
  def volume=(v)
    @volume=v>@maxvolume ? @maxvolume : v
  end
  def self.pour(a,b)
    need = b.maxvolume-b.volume
    if need > a.volume
      a.volume=0
      b.volume += a.volume
    else
      b.volume = b.maxvolume
      a-=need
    end
  end
end

点评

脚本肯定是没问题的,还有,以后我要是哪里说得不对或者是有疏漏还要多多提出宝贵意见啊。 第3问我表述得有点问题,在7L已经改正了。  发表于 2013-10-14 08:53
我要填坑!我要背单词!我要学日语!我要每天锻炼!
好吧呵呵= =
回复 支持 反对

使用道具 举报

Lv4.逐梦者 (版主)

梦石
0
星屑
9532
在线时间
5073 小时
注册时间
2013-6-21
帖子
3580

开拓者贵宾剧作品鉴家

9
 楼主| 发表于 2013-10-14 12:50:18 | 只看该作者
wingzeroplus 发表于 2013-10-13 10:45
原来数组居然不会被计算机复制一份,今天才知道,受教了(虽然怎么用过数组) ...

哪里不明白还是尽管在帖子里面提出来,类这个地方一定要好好理解。
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
60
在线时间
568 小时
注册时间
2012-9-7
帖子
611
10
发表于 2013-10-14 13:16:31 | 只看该作者
RyanBern 发表于 2013-10-14 12:50
哪里不明白还是尽管在帖子里面提出来,类这个地方一定要好好理解。

谢谢,我只是就随便感兴趣看看,况且我的作品也发布完了
作为一个不是以程序为赚钱的大叔,这些东西都是浮云

点评

那你就当挺我一下吧,哈哈。  发表于 2013-10-14 13:59
FTM正式版已经发布,点击图片开启传送门
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-11-24 13:00

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表