Project1

标题: [ARPG] 范例+教学 v0.1 [打印本页]

作者: hide秀    时间: 2009-6-3 08:46
标题: [ARPG] 范例+教学 v0.1
本次教程目的是教会大家 自己写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


作者: tommay    时间: 2009-6-3 08:51
沙发位,秀秀最近很活跃啊,义无反顾地抱走~
作者: 冷不冷    时间: 2009-6-4 03:21
提示: 作者被禁止或删除 内容自动屏蔽
作者: 后知后觉    时间: 2009-6-4 03:37
先收藏下{/hx}
作者: 月夜驃鱈    时间: 2009-6-5 03:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: 月夜驃鱈    时间: 2009-6-5 03:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: 天使喝可乐    时间: 2009-6-7 02:53
好东西..
作者: ONEWateR    时间: 2009-6-7 03:23
好久没见过秀秀君 {/se}
作者: ★_茄孓    时间: 2009-6-7 05:33
Good```非常好的教学,简洁简单!
作者: 小lim    时间: 2009-6-7 21:13
提示: 作者被禁止或删除 内容自动屏蔽
作者: DeathKing    时间: 2009-6-7 23:46
强文!

收藏下来关注一下。

看看以后是否会作出华丽的ARPG
作者: 魔幻预言者    时间: 2009-6-8 01:19
提示: 作者被禁止或删除 内容自动屏蔽
作者: OCTSJimmy    时间: 2009-6-10 10:10
留帖收藏!!……
作者: 越前リョーマ    时间: 2009-6-11 04:32
希望能有一些独立的系统……
这样可以事件和脚本结合……
作者: z0920605043    时间: 2009-7-26 12:09
提示: 作者被禁止或删除 内容自动屏蔽
作者: 632808263    时间: 2009-10-27 05:02
好东西先收下咯
作者: 幻耶    时间: 2009-10-28 19:57
待续在哪里?
作者: a8264704    时间: 2009-10-29 07:10
好是好哎
但是 #--------------------------------------------------------------------------
  # ● 检测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
我就看不懂了 - -
作者: 瓦沙尔    时间: 2009-11-17 20:17
提示: 作者被禁止或删除 内容自动屏蔽




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1