赞 | 0 |
VIP | 0 |
好人卡 | 0 |
积分 | 1 |
经验 | 170015 |
最后登录 | 2020-5-5 |
在线时间 | 2 小时 |
Lv1.梦旅人
- 梦石
- 0
- 星屑
- 55
- 在线时间
- 2 小时
- 注册时间
- 2006-11-10
- 帖子
- 931
|
6楼
楼主 |
发表于 2007-3-9 07:19:09
|
只看该作者
Scene 2 最初的Scene——Scene_Title
下面要读的是整个游戏的第一个场景:Scene_Title。
默认的SceneTitle有217行,在默认脚本中算比较长的了。它可以被分成两部分,一是执行时用到的main方法,一是main方法中调用的各种方法的定义。先来看main方法。
Scene_Title的Main方法从11行开始,到87行结束,忽略掉开头的战斗测试判定,它可以被分为标准的三部分。18~66行是第一部分(生成对象),68~79是第二部分(刷新循环),81~86是第三部分(释放对象)。
首先来看第一部分。
1-1,18~30行,载入数据库。
$data_actors = load_data("Data/Actors.rxdata") #——载入角色数据
$data_classes = load_data("Data/Classes.rxdata") #——载入职业数据
$data_skills = load_data("Data/Skills.rxdata") #——载入技能数据
$data_items = load_data("Data/Items.rxdata") #——载入道具数据
$data_weapons = load_data("Data/Weapons.rxdata") #——载入武器数据
$data_armors = load_data("Data/Armors.rxdata") #——载入防具数据
$data_enemies = load_data("Data/Enemies.rxdata") #——载入敌人数据
$data_troops = load_data("Data/Troops.rxdata") #——载入队伍数据
$data_states = load_data("Data/States.rxdata") #——载入状态数据
$data_animations = load_data("Data/Animations.rxdata") #——载入动画数据
$data_tilesets = load_data("Data/Tilesets.rxdata") #——载入图块数据
$data_common_events = load_data("Data/CommonEvents.rxdata") #——载入公共事件数据
$data_system = load_data("Data/System.rxdata") #——载入系统数据
在数据库中设定好的各类数据,被保存在Data文件夹下对应的文件中。load_data(”路径”)则是从指定文件中读取数据的方法。整句$data_actors = load_data("Data/Actors.rxdata")的完整含义是从Actors.rxdata文件中读取数据并赋给左边的变量$data_actors。(实际上这些$data_xxxx都是一些数组……)
$game_system = Game_System.new,生成一个新的Game_System实例。Game_System中存着一些系统附属数据,比如BGM等声音的控制,事件解释器,计时器等等。$game_system的状态是会被存进存档文件的,所以增加一种数据的话,写进Game_System也是一个不错的选择。
@sprite = Sprite.new
@sprite.bitmap = RPG::Cache.title($data_system.title_name)
这两句是在生成标题图片。形如@sprite的变量是实例变量(以@开头),它的作用范围限于类的内部,也就是说只在Scene_Title这个类里有用,并且只要是在这个类里,任何一个方法都可以调用它。第一句是生成一个新的Sprite对象(就是图片对象),第二句则是指定这个Sprite的bitmap(位图,就是具体要显示哪张图片)。RPG::Cache.title(“XX”)是将Graphics\title文件夹下名为“XX”的图片读入缓存,可以简单地理解为指定@sprite为这张图片。$data_system就是载入数据库时载入的系统数据,里面记录了数据库“系统”选项卡上的内容,而title_name这个方法则是获得在数据库“系统”选项卡里设定好的标题图片文件名。
s1 = "新游戏"
s2 = "继续"
s3 = "退出"
@command_window = Window_Command.new(192, [s1, s2, s3])
@command_window.back_opacity = 160
@command_window.x = 320 - @command_window.width / 2
@command_window.y = 288
37~43,生成一个选项窗口。
形如s1的变量是局部变量(小写字母或下划线开头,不能是保留字),它的作用范围限于方法的内部。在这里前四行可以合写为@command_window = Window_Command.new(192, [“新游戏”, “继续”, “退出”])。在选项比较少的情况下,分开写并没有什么太大的意义;但当选项多起来的时候,用变量使之简捷美观就变得很有必要了(比如Scene_Menu中的选项窗口)。
Window_Command.new是生成一个新的Window_Command对象(对于生成对象已经很熟了吧?),Window_Command是指令窗口的类,除了继承Window_Selectable中光标相关的各种方法之外,还有诸如disable_item等专为指令窗口设计的方便方法。
back_opacity是设定窗口背景透明度的方法(最小为0——全透明,最大为255——不透明),注意这个方法只有设定窗口背景的透明度,即默认skin的蓝色区域,周围白色的边框和内部的文字都是不透明的。(opacity是连边框一起的透明度,而contents.opacity则是窗口内容的透明度)
x是指定窗口左上角x坐标的方法,y是指定左上角y坐标的方法,width是指定窗口宽的方法,height则是指定窗口高度的方法。这四个值决定了窗口的形状和位置,可以在窗口的类的initialize方法中更改他们,也可以在调用该窗口的Scene中像上面这样更改他们。
@continue_enabled = false #——设置读档标志为不可用
for i in 0..3 #——0到3的for循环
if FileTest.exist?("Save#{i+1}.rxdata") #——若游戏文件夹下存在名为Save#{i+1}.rxdata的文件
@continue_enabled = true #——设置读档标志为可用
end #——if判定的end
end #——for循环的end
if @continue_enabled #——如果读档可用
@command_window.index = 1 #——设置命令窗口的索引为1
else #——否则
@command_window.disable_item(1) #——设置命令窗口选项为不可用
end #——if判定的end
47~59行是在做一个效果,就是如果存在存档文件,标题画面的“读取”选项可用,反之则呈灰色显示。其中@continue_enabled是个普通的实例变量,叫什么都可以,这里只是用它做一个标记方便下面的判断。
FileTest.exist?("路径/文件名")是判定指定路径下是否存在某文件的方法,其中文件名要打全名(png格式的图片在此也不能省略扩展名)。
如果一个变量只有真假值时,可以简略的用if @变量名代替if @变量名 == true,用if !@变量名代替if @变量名 == false。
Index和disable_item(index)都是Window_Command里面的方法(index是用attr_reader而不是def定义的),这个后面会说到。
接下来的四行比较简单:
$game_system.bgm_play($data_system.title_bgm) #——开始播放bgm
Audio.me_stop #——停止播放ME
Audio.bgs_stop #——停止播放BGS
Graphics.transition #——执行渐变
到Graphics.transition为止,整个生成对象部分就完结了。在生成对象部分中,生成了本Scene需要调用到的各种对象,包括窗口,精灵等。同时,一些想要在图像变化之前(随图像一起)开始播放的音乐之类也在此处设定。
注意,第一部分的内容也仅仅是“设定”,除了设定初始值之外,并不会直接运行。
接下来是第二部分,刷新循环。这部分比第一部分短很多,且在每个Scene中基本是一样的:
loop do #——循环
Graphics.update #——刷新画面
Input.update #——刷新输入信息
update #——调用update方法
if $scene != self #——当场景变换时
break #——中止循环
end #——if语句结束
end #——循环结束
Graphics.update是刷新画面的指令,让画面前进1帧,无论画面上是否有在活动的东西它都会刷新,可说是游戏中执行次数最多的一条指令。两次执行这条指令的间隔如果超过十秒,就会被程序认为是卡死从而强制关闭,弹出一个“脚本已经被备分”的对话框。如果出现这种情况时,请重启电脑或整理内存,关闭不必要的应用程序然后再测试,如果还出现,那就是程序里有死循环了。标准情况下,未开启平滑模式时,一秒钟会刷新20次画面,开启后一秒钟刷新40次。
Input.update是刷新输入信息,当某个Scene需要接受按键处理时,就要在loop do循环里加上这么一句,否则Scene是不会管你有没有在按键的。刷新输入信息跟刷新画面一样,也是一秒钟20次或40次。(据说世界上按键最快的人每秒能按键18次?……反正够用的了。=v=)
If $scene != self,是判定场景是否有变更的语句。Self是个伪变量,它指的是“当前方法的执行对象本身”,听起来有点绕口,实际上可以简单地理解为它就是指SceneTitle,而SceneMap中的self指SceneMap,WindowItem中的self指WindowItem。
当要变换场景时,使用$scene = Scene_XX.new,然后在这循环里由于$scene的值已变而self值未变,满足了$scene != self,于是执行了break(中断循环),程序往下进行来到第三部分:释放对象。
释放对象的内容也很短,一共四行:
Graphics.freeze
@command_window.dispose
@sprite.bitmap.dispose
@sprite.dispose
dispose方法是将对象释放的方法,将用过的窗口或其他对象用此方法释放掉以节约资源。如果没有将第一部分生成的对象全部释放,每次进入这个场景时都会再生成一次对象,长此以往就会大幅拖慢运行速度,严重时还会卡死。所以当游戏帧数掉的很厉害时,就要仔细检查一下每个Scene是否有把生成的对象全释放掉了。在第一部分生成了三个对象:选项窗口,精灵的图片和精灵本身,这里把他们全释放掉。注意sprite类对象要释放两次,一次是释放掉它的位图,一次是释放它本身。
Graphics.freeze前面已经说了,是冻结画面的指令,到下次Graphics.transition之前,画面上的一切变化都不会变更。把画面冻结住之后,我们即使释放掉了窗口或图片,它仍然会留在画面上,直到执行下一个Graphics.transition。同理,如果我们在这段期间显示了窗口或图片,它也不会立即在画面上显示出来,直到Graphics.transition之后才会显示。
这样main方法就结束了。不同的Scene有不同的main方法,但大致上都是可以分为这三段的,且后两段的格式基本都是通用的(特别是菜单类的Scene),虽然长,但并不复杂。
接下来是Scene_Title中调用的一些方法。首先是不可或缺的update方法,它负责窗口内容的变换和按键处理。Scene_Title的刷新方法还是比较简单的:
def update
@command_window.update
if Input.trigger?(Input::C)
case @command_window.index
when 0 # 新游戏
command_new_game
when 1 # 继续
command_continue
when 2 # 退出
command_shutdown
end
end
end
这里@command_window.update这句是调用Window_Command自带的update方法,先不去管它。下面if Input.trigger?(Input::C)是“当按键C被按下时”,这里的“按键C”并不是指键盘上的C键,而是指空格/回车键(其实C键也行-_-),具体的按键与键盘键位对应表可以在帮助文档中“游戏的操作方法”一节查看。接下来是一个case~when~end分歧语句,@command_window.index是取得光标位置的索引值(索引值为0~[选项个数-1]之间的整数)。Case语句跟上面的if语句合起来就是“按键按下时光标停在某选项上时”,而when下面则调用与该选项相对应的方法。
110~143,定义了开始新游戏的command_new_game方法。
$game_system.se_play($data_system.decision_se) #——播放系统“决定”的音效。
Audio.bgm_stop #——停止播放BGM
Graphics.frame_count = 0 #——重置画面计数器。这个是数帧数用的,游戏时间就是用帧数/速度算出来的。
$game_temp = Game_Temp.new #——生成新的临时对象
$game_system = Game_System.new #——生成新的系统附属数据对象
$game_switches = Game_Switches.new #——生成新的开关。所以直到选择新游戏那一刻,SceneTitle是无法调用事件里的开关或变量的,因为根本就没生成。
$game_variables = Game_Variables.new #——生成新的变量
$game_self_switches = Game_SelfSwitches.new #——生成新的独立开关
$game_screen = Game_Screen.new #——生成新的画面对象
$game_actors = Game_Actors.new #——生成新的人物对象。这个是数据库里的全部人物而非队中人物。
$game_party = Game_Party.new #——生成新的队伍,这回是队中人物了。
$game_troop = Game_Troop.new #——生成新的敌人队列。
$game_map = Game_Map.new #——生成新的地图对象。
$game_player = Game_Player.new #——生成新的主角,这个主要是处理事件启动判定等。
$game_party.setup_starting_members #——设置初期同伴,就是设定初期队伍中有哪些人。也就是在数据库——系统选项卡里左上角那里设置的人物。
$game_map.setup($data_system.start_map_id) #——设置人物初期位置,就是把人物放在标着S的白色小方块的那张地图上。所以当没有设置初期人物位置的时候会报错“找不到MAP000.rxdata”。
$game_player.moveto($data_system.start_x, $data_system.start_y) #——将主角放置在初期位置,就是放在“S”那格。
$game_player.refresh #——刷新主角
$game_map.autoplay #——切换地图的BGM与BGS
$game_map.update #——刷新地图,执行地图上的并行事件
$scene = Scene_Map.new #——切换到SceneMap。$scene值在这里改变后,本Scene的循环就会因$scene != self而中断。
147~158,定义的是读档的command_continue方法。
unless @continue_enabled #——除非读档标记为真。还记得前面用到的@continue_enabled么?作为实例变量,它可以在main之外的方法被调用。另外就是注意unless和if的区别……(我自己总搞混-_\\)
$game_system.se_play($data_system.buzzer_se) #——播放“冻结”的系统音效
return #——结束方法。Return之后,方法会直接结束,不去理会下面的指令。
end
$game_system.se_play($data_system.decision_se) #——播放“确定”的系统音效。
$scene = Scene_Load.new #——切换到读档画面
这个方法很简单,不过return除了结束方法之外还有别的用途,以后再讲。
162~171,定义了结束游戏的command_shutdown方法。
$game_system.se_play($data_system.decision_se) #——播放决定音效
Audio.bgm_fade(800) #——BGM淡出。参数“800”的单位是毫秒。
Audio.bgs_fade(800) #——BGS淡出
Audio.me_fade(800) #——ME淡出
$scene = nil #——场景转换为无。这样当SceneTitle的执行结束,再次回到Main方法时,由于没有Scene可显示,会直接结束。
最后一部分的battle_test是战斗测试时用的(所以战斗测试并不装载地图,仅由SceneTitle和SceneBattle组成),平时用不到,也没有太大的阅读价值,故不予解读。(实际上无非是装载数据库,设定几个值和场景转换而已,有兴趣也可以看看)
这样关于Scene_Title的解说就结束了。Scene_Title是游戏最初的场景,在了解了它的构造和功能之后,我们可以着手对其进行各种各样的蹂躏……呃,是改造。比如添加标题画面的选项,改用图片标题菜单,设置标题画面的各种特效(滚动,流星雨,etc.),或者干脆删除不必要的部分直接使用事件做标题画面,都是通过改造这个Scene_Title来完成的。 |
|