扫描二维码关注官方公众号
返回列表
+ 发新帖
查看: 129|回复: 0

[转载发布] [ARPG] 范例+教学 v0.1

[复制链接]
累计送礼:
0 个
累计收礼:
0 个
  • TA的每日心情
    开心
    昨天 18:01
  • 签到天数: 114 天

    连续签到: 4 天

    [LV.6]常住居民II

    2338

    主题

    403

    回帖

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    VIP
    6
    卡币
    10622
    OK点
    16
    推广点
    0
    同能卷
    0
    积分
    13391

    灌水之王

    发表于 2024-4-19 17:07:11 | 显示全部楼层 |阅读模式
    本次教程目的是教会大家 自己写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


                 本帖来自P1论坛作者hide秀,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg.blue/forum.php?mod=viewthread&tid=126277  若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
    天天去同能,天天有童年!
    回复 送礼论坛版权

    使用道具 举报

    文明发言,和谐互动
    文明发言,和谐互动
    高级模式
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    关闭

    幸运抽奖

    社区每日抽奖来袭,快来试试你是欧皇还是非酋~

    立即查看

    聊天机器人
    Loading...

    QQ|Archiver|手机版|小黑屋|同能RPG制作大师 ( 沪ICP备12027754号-3 )

    GMT+8, 2025-3-15 00:08 , Processed in 0.139710 second(s), 53 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.

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