cocos2d-x mruby学习笔记--入门

本文介绍如何利用Ruby和Cocos2d-x快速开发移动游戏应用,通过脚本实现即时预览,避免繁琐的编译流程,并展示了具体的实现步骤和技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

感谢cocos2d-x团队 http://www.cocos.com/doc/
感谢matz https://github.com/mruby/mruby
当然最主要是感谢tkyaji https://github.com/tkyaji/cocos2d-ruby
一直对cocos2d-x项目有点兴趣,但一直懒没有认真去学习,当然我个人认为最主要的原因在于编译过程,当我修改了一点点东西想直观的看看修改后的运行结果时。不得不说大的c++项目真真实实是个杯具。
在mruby发布后,我曾经自己做过cocos2d-x mruby的桥接,当然后来不了了之了。还是因为太懒呀,在tkyaji半年前发布了cocos2d-ruby后,我一直毫不知情,不然我肯定会比现在勤奋一点点。。
cocos2d-ruby基于mruby v1.1.0 和cocos2d-x v3.4,当然自己可以去升级。我个人估计没这个本事了。
下载。按简单的提示cocos run -p android就可以运行一个android项目了,但这并不是真正令人兴奋的地方,令人兴奋的地方在于cocos2d-x+mruby只是提供了一个运行环境,所有的工作都可以通过脚本来完成。而无须编辑和发布你的应用,我的办法是测试的时候直接adb push 脚本文件到android可写目录。。然后运行应用。完全可以说是所见即所得--哪怕只改动了一个小地方。
事实上对于android应用,可写目录是/data/data/packageName/files/,但对于我们测试,没有root的机器可以直接使用sdcard,比如我个人就是这样做的。以下假设pageageName=com.blackant.mytest
#编译项目。如果不改动C++/C源码,整个项目就只需要这一次编译了。但这只是一个美好的梦想,事实上,我们还是需要编译。因为我们要扩展mruby
cocos complie -p android
#安装到手机 请先确保手机usb调试已开启。如果你和我一样不幸经常会断开adb连接,换个手机试试吧,我换了两台,第三台是小米,总算不自动断开连接了
adb install /Users/anfeng/blackant/MyTest/runtime/android/MyTest-debug.apk 
#开始运行应用。
adb shell am start -n com.blackant.mytest/org.cocos2dx.ruby.AppActivity
这个世界美好了。事实上脚本文件现在做为资源打包在apk中。我们每次修改都要重新打包。。。这太残酷了,我们的目标是将脚本文件上传到一个可读写的目录。然后修改脚本后直接看到效果,不需要再编译与安装。go....


#也许你的sdcard叫别的名字自己随意吧
adb shell  mkdir /storage/sdcard0/blackant
adb push src /storage/sdcard0/blackant


然后修改frameworks/runtime-src/classes下的AppDelegate.cpp
bool AppDelegate::applicationDidFinishLaunching()
{
    RubyEngine* engine = RubyEngine::getInstance();
    //运行时应该解压文件到这个可写目录
    //ScriptEngineManager::getInstance()->setScriptEngine(engine);
    //std::string basePath= FileUtils::getInstance()->getWritablePath();
    //engine->addSearchPath(basePath.c_str());
    //测试时随便push文件 到一个可写目录
    engine->addSearchPath("/sdcard/blackant");
    //原来使用的文件目录
    //engine->addSearchPath("src");
    engine->executeScriptFile("main.rb");
   
    return true;
}
这时你不得不编译/卸载/安装一次了
cocos complie -p android
adb uninstall com.blackant.mytest
adb install /Users/anfeng/blackant/MyTest/runtime/android/MyTest-debug.apk 


然后每次修改src目录下的rb文件(你也可以放在任何你喜欢的目录下。src只是沿用的cocos2d-x ruby习惯)后直接push到手机,重新启动一下应用即可
adb push src /storage/sdcard0/blackant
adb shell am force-stop com.blackant.mytest
adb shell am start -n com.blackant.mytest/org.cocos2dx.ruby.AppActivity


现在这个世界美好了。。。。但是直正到实用。我们还有一点点事儿要做,整个项目中比较重要的是两个目录
framework/cocos2d-x/external/ruby/mruby 是mruby原生
framework/cocos2d-x/cocos/scripting/ruby-bindings 下面是cocos-mruby 的扩展
现在可以试试mruby-curl扩展了。为什么用这个?因为cocos2d-x本身就含有libcurl.a
mattn自己扩展了mruby-curl 见https://github.com/mattn/mruby-curl,虽然作者没有声明但这个扩展事实上依赖于他的另一个扩展https://github.com/mattn/mruby-http
其实对于我们来说除了编译期。我们不需要h文件,因为我们需要的只是ruby脚本引擎,经过一点的修改后
同时另外我喜欢的扩展有thread和io.都是从github下载回来的,需要一点点小的修改就行了
然后在framework/cocos2d-x/external/ruby/mruby/mrbgems/gem_init.c的mrb_init_mrbgems方法中加入新的扩展
mrb_mruby_http_gem_init(mrb);
mrb_mruby_curl_gem_init(mrb);
mrb_mruby_thread_gem_init(mrb);
mrb_mruby_io_gem_init(mrb);
不如意思,又要编辑了。。。。
现在我们的引擎有了新的能力
====虽然简单,但是很酷哟,这意味着我们的应用有了多线程访问网络的能力
@queue= Queue.new 
@mutex= Mutex.new
Thread.start(@mutex,@queue)  do | m,q |
         Thread.sleep(2)
         body=Curl.get("http://www.baidu.com").body
         m.synchronize do 
            q.push(body) 
         end
end
如果你喜欢其实的扩展,可以自己试试,并不复杂
另一个重要的目录是framework/cocos2d-x/cocos/scripting/ruby-bindings 
这儿的auto目录估计是作者用脚本生成的,有一些小小的错误。。。。但并不重要。可以自己改一下。比如
proj.android/android.mk中指示链接了作者扩展的所有文件,如下所示
LOCAL_SRC_FILES := ../manual/RubyEngine.cpp \
          ../manual/RubyBasicConversions.cpp \
          ../manual/ruby_global_manual.cpp \
          ../auto/ruby_cocos2dx_3d_auto.cpp \
          ../auto/ruby_cocos2dx_audioengine_auto.cpp \
          ../auto/ruby_cocos2dx_auto.cpp \
          ../auto/ruby_cocos2dx_experimental_auto.cpp \
          ../auto/ruby_cocos2dx_experimental_video_auto.cpp \
          ../auto/ruby_cocos2dx_spine_auto.cpp \
          ../auto/ruby_cocos2dx_ui_auto.cpp \


但事实上在 manual/RubyEngine.cpp中并没有提供除了auto以外的扩展在bool RubyEngine::init(void)中。作者的愿意可能是
    register_all_global_manual(_mrb);
    register_all_cocos2dx(_mrb);
    register_all_cocos2dx_ui(_mrb);
    register_all_cocos2dx_audioengine(_mrb);
    register_all_cocos2dx_experimental(_mrb);
    register_all_cocos2dx_3d(_mrb);
    register_all_cocos2dx_spine(_mrb);
    register_all_cocos2dx_experimental_video(_mrb);
到了这个,又有个小小的错误了,在manual/RubyBasicConversions.h中定义了
static struct mrb_data_type ruby_cocos2dx_Ref_type = {"cc::Ref", ruby_cocos2dx_Ref_finalize};
由于该文件被多次引用也就是说。到时候ruby_check_data_type会导致你的CC::Ref不是CC::Ref.因为它们是完全独立的了。需要去除static定义
还有一些小错误,比如CC::Spawn没有定义create,需要
mrb_define_class_method(mrb, rclass, "create", ruby_cocos2dx_Spawn_create_static, ARGS_REQ(1));
当然,还可能有其它的问题,这个是我正好遇到了而已
然后。。。。。。再编译。。。。好了,至少到目前为此我们不需要编译了
本看个真实的示例吧,美术素材来自http://blog.youkuaiyun.com/kuloveyouwei/article/details/12912875,花了我三分才下到的。。。。



class MainScene < CC::Layer
  def self.scene
    scene = CC::Scene.create 
    layer = MainScene.create
    scene.add_child(layer)
    return scene
  end
    
  def initialize
    begin
       @director = CC::Director.get_instance


       @visible_size = @director.get_visible_size
       @origin = @director.get_visible_origin


       @texture= @director.get_texture_cache
       @sprite_frame_cache=CC::SpriteFrameCache.get_instance


       @sprite_frame_cache.add_sprite_frames("sailing_boat-hd.plist")
       @sprite_frame_cache.add_sprite_frames("seagull-hd.plist")


       @bg_sprite=CC::Sprite::create("background_568-hd.png");
       bg_size=@bg_sprite.get_content_size
       @scale_x=@visible_size.width.to_f / bg_size.width
       @bg_sprite.set_scale_x(@scale_x )
       @scale_y =@visible_size.height.to_f / bg_size.height
       @bg_sprite.set_scale_y( @scale_y)
       @bg_sprite.set_position(@visible_size.width / 2 +@origin.x , @visible_size.height / 2 + @origin.y)


       self.add_child(@bg_sprite,0)


       @cloud=CC::Sprite.create("background_cloud_1-hd.png")
       @cloud.set_anchor_point(CC::Vec2.new(0,0))
       px=0+@origin.x
       
       py=@origin.y+ @visible_size.height  -  @cloud.get_content_size.height  *  @scale_y  
       @cloud.set_position( px , py)
       self.add_child(@cloud,1)


      @cloud_flip=CC::Sprite::create("background_cloud_1-hd.png");
      @cloud_flip.set_anchor_point(CC::Vec2.ZERO);
      #垂直翻转
      @cloud_flip.set_flipped_y(true);
      @cloud_flip.set_opacity(100);
      py=py - @cloud.get_content_size.height 
      @cloud_flip.set_position(px, py);
      self.add_child(@cloud_flip,1) 


      @island=CC::Sprite::create("island-hd.png")
      content_size=@island.get_content_size
      px=(@visible_size.width- content_size.width) / 2 + @origin.x + 100
      py=@origin.y+ @visible_size.height  -  @cloud.get_content_size.height  *  @scale_y  
      @island.set_position(px,py)
      self.add_child(@island,1)


      @island_clone=CC::Sprite::create("island-hd.png")
      @island_clone.set_flipped_y(true)
      @island_clone.set_opacity(100)
      @island_clone.set_position(px,py-content_size.height)
      self.add_child(@island_clone,1)


      #需要事先有精灵帧的缓存
      @board=CC::Sprite.create_with_sprite_frame_name("sailing_boat1.png")
      @board_px=px+10
      @board_py=py-20
      @board.set_position(@board_px, @board_py)
      self.add_child(@board,2)
      




      @beach=CC::Sprite.create("beach_adornment-hd.png")
      content_size=@beach.get_content_size
      @beach.set_position(@visible_size.width / 2 +@origin.x, content_size.height / 2 +@origin.y)
      @beach.set_scale_x(@scale_x)
      self.add_child(@beach,3)


      
      @wave=CC::Sprite.create("background_sea1_568-hd.png")
      @wave.set_position(@origin.x+ @visible_size.width / 2 , content_size.height * @scale_y +@origin.y + @wave.get_content_size.height / 2 )
      @wave.set_scale_x(@scale_x)
      self.add_child(@wave,3)
    
      
      menu_item=CC::MenuItemImage.create("button_play-hd.png", "button_play_s-hd.png", Proc.new {|sender|
          p __FILE__
       }).set_position(@visible_size.width / 2 + @origin.x, @origin.y + 50).
          set_scale_x(@scale_x).
          set_scale_y(@scale_y)
      menu=CC::Menu.create(menu_item).
          set_position(CC::Vec2.ZERO)
      self.add_child(menu,4)


       self.schedule_once(Proc.new {|dt| 
        self.board_action
        self.wave_action
        },0.1,"board_action_schedule")
    rescue StandardError=>exp
      p exp.message
      p exp.backtrace.to_s
    end
  end


  def wave_action
     cache=@director.get_texture_cache
     @texture1= cache.add_image("background_sea1_568-hd.png")
     @texture2= cache.add_image("background_sea2_568-hd.png")
     
     point=@wave.get_position
     px =point.x
     py=point.y
     step=@wave.get_content_size.height / 2
     move1=CC::MoveTo.create(3.0, CC::Vec2.new(px,py-step))
     fadeIn=CC::FadeIn.create(3.0)
     spawn=CC::Spawn.create(move1,fadeIn)
     func1=CC::CallFunc.create(Proc.new{
          @wave.set_texture(@texture2)
      })


     move2=CC::MoveTo.create(3.0,CC::Vec2.new(px,py))
     fade_out=CC::FadeIn.create(3.0)
     spawn2=CC::Spawn.create(move2,fade_out)


     func2=CC::CallFunc.create(Proc.new{
          @wave.set_texture(@texture1)
      })


     sequence =CC::Sequence.create(spawn,func1,move2,func2)
     forever=CC::RepeatForever.create(sequence)
     @wave.run_action(forever)
  end
  
  def board_action
      #船儿使用RepeatForever来动
      caches=CC::SpriteFrameCache.get_instance
      frames=[]
      1.step(3) do |i|
        frameName="sailing_boat%d.png"%[i]
        frames<< caches.get_sprite_frame(frameName)
      end
      animation=CC::Animation.create_with_sprite_frames(frames,0.2,1)
      animate=CC::Animate.create(animation)
      
      #移动
       content_size=@board.get_content_size
       pos=CC::Vec3.new(@origin.x, @board_py,2)
       move_action =CC::MoveTo.create(10,pos)
       flipx_true=CC::FlipX.create(true)
       pos=CC::Vec3.new(@origin.x+@visible_size.width,@board_py,1)
       move_right_action=CC::MoveTo.create(15,pos)
       flipx_false=CC::FlipX.create(flipx_false)
       pos=CC::Vec3.new(@board_px,@board_py,2)
       move_left_action=CC::MoveTo.create(5,pos)
       sequence=CC::Sequence.create([move_action,flipx_true,move_right_action,flipx_false,move_left_action])


      spawn=CC::Spawn.create([sequence,animate])
      forever=CC::RepeatForever.create(spawn)
      @board.run_action(forever)
  end #def board_action




   


end #end of class


你可以和我一样修改一点点,push一点点。然后restart applicatgion......个人感觉用脚本来写应用还是挺不错的。
=====,为什么用ruby而不是lua...或js.......,仅仅因为我喜欢。而且很很重要的一点是。ruby太漂亮了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值