赞 | 0 |
VIP | 25 |
好人卡 | 0 |
积分 | 1 |
经验 | 126953 |
最后登录 | 2020-5-5 |
在线时间 | 39 小时 |
Lv1.梦旅人 粉蜘蛛秀秀
- 梦石
- 0
- 星屑
- 76
- 在线时间
- 39 小时
- 注册时间
- 2007-6-4
- 帖子
- 384
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本次教程目的是教会大家 自己写ARPG
熟悉RGSS的 利用RGSS框架 便很快就能做出一个像模像样的来
我要教大家的方法 也是利用现有的RGSS框架来做 尽可能使用最简单最高效的方法
其实也是对RGSS脚本的应用和改造
首先看范例:
http://rpg.blue/upload_program/d ... �v0.1_124418974.rar
v0.1 版本里 包含以下内容
1.按键盘的 A 键实现角色的近身攻击
2.按键盘的 S 键实现角色的跳跃
3.按住键盘的 D 键实现蓄力攻击(时间为30贞)
这些内容实现了ARPG主角的一些基本要素 大概脚本加起来也就200多行而已
接下来就来由浅入深的来教大家{/cy}
开始之前先说明一下制作v0.1里面需要关系到的默认脚本
以及一些很好用的方法
1.Game_Character
2.Game_Player
3.Game_Event
4.Game_Map
5.Sprite_Character
6.RPG::Sprite
1-4可以看成1类
Game_Character 是 Game_Player 和 Game_Event 的父类
很好理解 Game_Player 是主角 Game_Event 是事件
他们共用的一些方法 全部写在了父类 Game_Character 里
这里详解一下
move_down
move_up
move_left
move_right
move_random
这四个方法分别是 向下,向上,向左,向右,移动,随即方向移动
调用一次就一动一格(地图上的格子)
*切记:只有满足条件的时候才调用 默认是在update中 判断到了按键才调用
此外还有
move_lower_left 左下移动
move_lower_right 右下移动
move_forward 前进一步
move_backward 后退一步
turn_down 面向下
turn_up 面向上
turn_right_90 右转90度
turn_random 随机变换方向
move_toward_player 接近主角
等等方法 大同小异 这里就不作解释了
jump(x_plus, y_plus)
是跳跃 参数分别是x增加值和y增加值 也就是跳跃的坐标
如 jump(0,1) 就是向下跳跃1格
moving? 和 jumping?
用来判断是否在移动中 和 是否在跳跃中
一般写在循环中
if moving?
.....
end
判断到在跳跃 然后执行一些代码(如在移动中 按esc键无效)
moveto(x, y)
是直接跳转到坐标x,y 就是事件里面的 指定xx事件到坐标x,y
screen_x
screen_y
screen_z
角色行走图 显示在屏幕上的位置
这个3个方法 是要在update里面每贞调用的
只要 screen_x和screen_y 发生变化 那么行走图就会动起来了
所以说 调用了move_up等 方法之后 这些值就会发生变化
如果有兴趣想知道怎么回事 请看以下内容 不看也没有关系
具体地说 也就是
调用 move_up 方法 -> 改变了@y 值 ->
update中 判断到了 @y*128 和 @real_y 坐标不一致 ->
开始更新@real_y坐标 直到一致 -> @real_y坐标变化中 -> screen_y 也跟着变化->
结果人物动了起来
具体实现方法 希望大家去按顺序看
move_up
update_move
screen_x,screen_y
Sprite_Character类的 update 方法
就可以明白怎么回事了
screen_z 方法是z坐标 当然 是根据y坐标来变化的
y坐标越小 z值越大 所以看到的是 下面的人永远挡住上面的人
terrain_tag
是获取当前坐标的地形标记 很好用的一个方法
可以在update中捕获地形标记
如 if terrain_tag == 1
...
end
当前所在地形标志为1的话 则....(如遇敌了 更换遇敌列表)
接下来是Game_Map类
简单的说 这个类里面是包含了当前地图的所有信息
比如 有多少图块 多少事件 以及主角等等
初始化方法的时候 就把这些全部加载了
update里面则 一起更新
所以Game_Map类里 可以很方便的 捕捉到当前地图上的各个信息
也有几个跟地图有关的方法
width
获取地图宽度
scroll_down(distance)
向下滚动 distance 则是滚动距离 1为1格
scrolling?
判断是否在滚动中
最后要说的是
Sprite_Character 和 RPG::Sprite
RPG::Sprite 是 Sprite_Character 的父类
而 RPG::Sprite 的父类 是 Sprite
我们都知道要显示图片 都是要用 Sprite.new
生成一个精灵对象 然后指定bitmap属性 为其指定一张位图
然后再把x,y坐标设定下 就可以显示了
循环中把其x,y变化 还能做出动画效果
但是rgss为什么要写一个 RPG::Sprite 类呢
我们打开F1 搜索 RPG::Sprite 可以看到里面的大概
whiten
弱白色地闪烁
animation(animation, hit)
显示一个动画
loop_animation(animation)
循环显示一个动画
effect?
是否动画效果中..
以上等等方法
都是和动画有关,这下才明白 其实是为了更好更方便的使精灵支持动画而写的类
之所以支持动画 所以RPG::Sprite对象需要在循环中调用其update方法
Sprite_Character 类则是 RPG::Sprite 继承而来
所以我们的ARPG要在行走图身上显示动画 就必须从这里下手
* 我想说的是 我们终于可以切入正题了 *
我们首先要做的效果主角是 按S键跳跃
思路是 按键时判断主角的方向 -> 决定往哪里跳
既然是主角要跳跃 那一定是找Game_Player类
我们找到 Game_Player 的update方法
可以看到 默认写了一些 如
case Input.dir4
when 2
move_down
when 4
move_left
when 6
move_right
when 8
move_up
end
这样的脚本
很清楚地表达了 按某键->判断方向->实行移动
我们也跟着这个思路 来实现下 按S键->判断方向->实现跳跃
if SmInput.trigger?(SmInput::S) and not jumping?
case @direction
when 2
jump(0, 1)
when 4
jump(-1, 0)
when 6
jump(1, 0)
when 8
jump(0, -1)
end
end
把以上脚本写入到循环中去 然后按S键盘 是否跳跃了呢?
jump方法之前已经解释过了 参数则是跳跃的坐标
SmInput.trigger?(SmInput::S) and not jumping?
表示按了S键 并且 没有在跳跃中
SmInput.trigger?(SmInput::S)
是夏娜的全键盘脚本用来判断按下S键盘
没有办法 默认的Input模块不支持S键...没办法 - -
接下来要实现的是按A键攻击
思路是这样的
按A键->主角播放攻击动画->判断到到动画命中桢的瞬间是否击中敌人->
击中的话显示敌人挨打动画
我们首先要做的是要让我们角色能够显示动画
刚才说过这样一句话
所以我们的ARPG要在行走图身上显示动画 就必须从Sprite_Character下手
我们打开 Sprite_Character 的update方法
可以发现以下代码
if @character.animation_id != 0
animation = $data_animations[@character.animation_id]
animation(animation, true)
@character.animation_id = 0
end
如果 @character.animation_id != 0
则播放id为 @character.animation_id 的动画
如此一来就变得简单了 我们只要在$game_player循环里
判断按A键 -> 设置 @animation_id 就可以了
立刻动手把 Game_Player的update里面 加入以下代码
if SmInput.trigger?(SmInput::A) and not jumping? and !@effect and !moving?
case @direction
when 2
@animation_id = 102
when 4
@animation_id = 103
when 6
@animation_id = 104
when 8
@animation_id = 105
end
end
这段代码就不解释了 应该很容易理解吧
根据方向设置不同的动画id
动画请看范例工程里面的 数据库动画 102号至105号
如此一来 按了A健就会播放攻击动画了
接下来要判断动画的命中贞
我在数据库里面设置了第9贞 也就是击中目标的瞬间贞
我们顺便也记下动画总共是14贞吧(后面有用- -)
接下来我们要先写个方法来判断当前方向邻边是否有敌人
Game_Map类里面不是很好获取地图信息么
那么我们就写在Game_Map类里面好了
在Game_Map类里面写入以下方法
#--------------------------------------------------------------------------
# ● 检测player周围是否存在事件(返回id)
#--------------------------------------------------------------------------
def check_player_around(dir)
id = 0
@events.each do |key,value|
case dir
when 2
if (value.x == $game_player.x and value.y == $game_player.y+1)
id = key
break
end
when 4
if (value.x == $game_player.x-1 and value.y == $game_player.y)
id = key
break
end
when 6
if (value.x == $game_player.x+1 and value.y == $game_player.y)
id = key
break
end
when 8
if (value.x == $game_player.x+1 and value.y == $game_player.y-1)
id = key
break
end
end
end
return id
end
其作用是 输入参数方向(dir)
自动会在该方向的邻边上去去扫描地图上所有事件
如果判断到有该方邻边有事件存在 则返回这个事件的id
如果没有找到 则返回 0
返回id的好处是 方便在这个事件上播放挨打动画
这个方法写好之后 调用就变得异常简单
$game_map.check_player_around(dir)
只要输入参数dir 2:下 4:左 8:上 6:右
就会判断出一个结果
接下来我们要深入到 动画播放到命中贞来判断 是否存在敌人(调用)
我们把F1里面的RPG::Sprite类里面的 update方法复制到脚本中去
然后找到这段:
if @_animation != nil and (Graphics.frame_count % 2 == 0)
@_animation_duration -= 1
update_animation
end
这段代码意思是:
如果动画对象 @_animation 存在 (并且是2贞执行一次)
@_animation_duration(动画时间) -1
然后更新动画 update_animation
这里我们首先可以看出 动画是每2贞更新一次的
也就是说 数据库动画 设定了10贞 实际上画面上是需要20贞来显示
@_animation_duration 是动画时间 也就是数据库里面的贞数
我们攻击动画为14贞(刚才已经记住了 - - )
我们要做的是 要在 第9贞来判断
相反就是还剩下5贞的时候(14-9)
我们把它变为下代码
if @_animation != nil and (Graphics.frame_count % 2 == 0)
@_animation_duration -= 1
if [102,103,104,105].include? @_animation.id
if @_animation_duration == 5
id = $game_map.check_player_round($game_player.direction)
if id != 0
$game_map.events[id].animation_id = 4
end
end
end
update_animation
end
if [102,103,104,105].include? @_animation.id
判断 动画id必须是 102至105
@_animation_duration == 5 表示还剩下5贞的时候
id = $game_map.check_player_around($game_player.direction)
表示 根据主角方向判断邻边是否有敌人
if id != 0
$game_map.events[id].animation_id = 4
end
如果存在敌人 就播放挨打动画(4号)
试试看,是否实现了涅?{/cy}
最后是 按住键盘的 D 键实现蓄力攻击的实现
基本思路是
判断按键蓄力->蓄力过程在主角身让显示循环动画->蓄力成功->
消失循环动画->主角身上建立一个可穿透的事件(火焰弹?)->
火焰弹朝主角所在的方向快速的移动->火焰弹在移动的过程中接触到敌人->
消失事件(火焰弹)->敌人身上播放挨打动画->如果没有接触到敌人->
飞到地图边缘则自动消失
首先我们要实现的是 蓄力
我们要在Game_Player类里面 设置一个@power的变量
用来记录蓄力的强度
接着我们 打开全键盘脚本
看 self.repeat?(rkey) 这个方法
全键盘里面的repeat?方法 是判断安下某键一定时间 默认为20贞
它会判断是否达到20贞 如果达到了 则返回true
我们这里来改造一下
我们把它改造成 按下就有效 松开就无效
其实我们只要把以下这段删除
if $R_Key_Repeat[rkey] >= 20
$R_Key_Repeat[rkey] = 0
return true
else
return false
end
改成
return $R_Key_Repeat[rkey]
就可以了
这样可以返回按了多少贞
然后再Game_Player 的update 加入判断就可以了
if SmInput.repeat?(SmInput::D) > 0
@power += 1
@loop_animation_id = 92
@looping_ani = true
else
if @power >= 30
fire(@direction)
end
@power = 0
@looping_ani = false
end
当按着的时候 @power 一值增加
挡松开的时候 只要 @power >= 30 则蓄力成功
@loop_animation_id = 92 是循环动画
只要往 Game_Character 的 update 里面添加上循环动画判断就可以支持了
@character.effect = self.effect?
# 循环动画
if @character.loop_animation_id != 0
loop_animation($data_animations[@character.loop_animation_id])
@character.loop_animation_id = 0
end
if @_loop_animation != nil and @character.looping_ani == false
stop_loop_animation
end
加入一个停止动画的方法
def stop_loop_animation
loop_animation(nil)
end
Game_Character 里加入几个实变量并设属性
class Game_Character
attr_accessor :effect
attr_accessor :loop_animation_id
attr_accessor :looping_ani
attr_accessor :move_speed
attr_accessor :through
alias :ori_initialize :initialize
def initialize
ori_initialize
@effect = false
@loop_animation_id = 0
@looping_ani = false
end
end
如此就能播放循环动画了 如果松开D键盘 @power 大于30 则发动成功
if @power >= 30
fire(@direction)
end
fire 方法具体则是建立一个名为"火"的事件 然后顺着方向移动(发射)
地图建立事件的方法其实就是在
Game_Map的 hash对象 @events 里面添加一个 RPG::Event 对象
然后把一些基本属性建立起来就可以了
地图上删除一个事件 也是一样的 把hash对象@events里面删除掉就可以了
具体请看 add_event 方法 这里就不多说了
当然建立和删除之后需要重载以下地图的 @spriteset对象
只要释放一次 建立一次即可 参考 reload_spriteset 方法
建立完之后需要发射 也就是移动事件
其实很简单 需要移动几格 就执行几次 移动的方法
def fire(d)
# 根据player方位来建立名为[火]的事件 然后顺着player方向移动
# 移动次数根据离map边缘坐标决定
name = "fire"
case d
when 2
id = $game_map.add_event(@x,@y,"火",name)
($game_map.height-@y).times{$game_map.events[id].move_down}
when 4
id = $game_map.add_event(@x,@y,"火",name)
@x.times{$game_map.events[id].move_left}
when 6
id = $game_map.add_event(@x,@y,"火",name)
($game_map.width-@x).times{$game_map.events[id].move_right}
when 8
id = $game_map.add_event(@x,@y,"火",name)
@y.times{$game_map.events[id].move_up}
end
end
这是完整的fire方法
id = $game_map.add_event(@x,@y,"火",name)
在当前主角的坐标 建立名为火的事件 name 则是行走图文件名
返回是这个事件的id
($game_map.height-@y).times{$game_map.events[id].move_down}
根据id 来实现 事件的移动
移动次数为 $game_map.height-@y 次
这样是表示 移动到地图的边界
最后....只要在Game_Map的update中 捕捉到 "火"的事件存在
并且判断是否击中敌人 或者 走到了边界
判断是否击中敌人 其实只要判断坐标是否一致
一般情况下用@x,@y 来判断
@x,@y 1表示1格 但是 这里不能用这个来判断
应该移动中变化的是 real_x 和 real_y 因为 @x,@y是直接指定的
所以只要判断 事件"火"的 real_x 和 real_y 移动中 是否有事件的坐标和其相等
判断到就在其时间上播放挨打动画 并删除"火"的时间
没有判断到 移动到边界就 删除"火"的事件
具体代码如下:
#--------------------------------------------------------------------------
# ● update 别名
#--------------------------------------------------------------------------
alias :ori_update :update
def update
ori_update
# 检测地图上是否存在名为[火]的事件
if (id=check_event_name("火")) != 0
# 获取 [火] 的real_x和real_y
real_x,real_y = @events[id].real_x,@events[id].real_y
# 检测地图上是否有事件被[火]碰撞(击中),返回事件id
target_id = check_event_id(real_x,real_y)
# 击中的情况下
if target_id != 0
# 事件上播放挨打动画
show_event_ani(target_id,4)
# 删除[火]的事件
delete_event(id)
return
end
# 碰到边界消失
case @events[id].direction
when 4
if @events[id].real_x == 0
delete_event(id)
end
when 6
if @events[id].real_x >= $game_map.width*128-128
delete_event(id)
end
when 8
if @events[id].real_y == 0
delete_event(id)
end
when 2
if @events[id].real_y >= $game_map.height*128-128
delete_event(id)
end
end
end
end
您对于制作ARPG是否已经入门了呢?{/wx}
未完待续...
附上范例:
http://rpg.blue/upload_program/d ... �v0.1_124418974.rar
|
|