公司要开始做小游戏了,经过研究讨论之后决定采用Laya作为开发引擎,本身是做Unity3D开发的,学习成本很低,Laya的编程方式和Unity很相似,对Unity开发人员来说没有什么难度,从这篇文章开始就记录一下学习以Laya制作demo。

使用Unity导出场景资源

前去Laya官网下载对应版本的Unity导出插件,在Unity中搭建好场景,并一键导出。(注意Laya并不支持Unity的材质,需要将Unity的材质转换成Laya的材质)


Laya 的资源一般存放在工程路径下bin/res Unity导出的场景文件也放在这个场景即可。

加载Scene场景

Laya的开发方式和Unity一致,因此创建一个GameStart脚本加载3D场景,并将3D场景添加至舞台。因为舞台上存在着一些UI,因此我们需要将3D场景加载到第0号节点,以免3D场景遮挡了其它的UI

  onAwake(): void{ Laya3D.init(0, 0);Laya.Scene3D.load("res/LayaScene_Main/Conventional/Main.ls",Laya.Handler.create(this,this.onSceneLoadComplete));Laya.stage.on(AppConst.MoveColumn,this,this.moveColumn);Laya.stage.on(AppConst.CreatePrticle,this,this.onSpawnPrticle)Laya.stage.on(AppConst.RePlayGame,this,this.onRePlayGame)}onSceneLoadComplete(scene):void {//加载完成获取到了Scene3dLaya.stage.addChildAt(scene,0);this.scene=scene;//加载完成获取到了Scene3dLaya.stage.addChildAt(scene,0);this.scene=scene;//获取摄像机var camera = scene.getChildByName("Main Camera");//清除摄像机的标记camera.clearFlag = Laya.BaseCamera.CLEARFLAG_SKY;camera.addComponent(CameraFollow)var parent=scene.getChildByName("Parent")this.column=parent.getChildByName("Column")parent.addComponent(Rotation);var player=scene.getChildByName("Player")//拖尾特效var trail=scene.getChildByName("TrailRender")player.addChild(trail);trail.transform.localPosition=new Laya.Vector3();//默认为(0,0,0)player.addComponent(PlayerController)var platform=parent.getChildByName("Platform");platform.active=falsethis.owner.addComponent(SpawnPlatform).init(platform,parent); var particle=scene.getChildByName("Particle")this.particle=Laya.Sprite3D.instantiate(particle,scene);}

让柱子跟随手指转动起来

实现让圆柱跟随拖拽旋转逻辑很简单,只需要捕获鼠标按下、拖动以及抬起三个事件,做相应的逻辑处理即可。
注意 Laya.stage.on(Laya.Event.MOUSE_OUT,this,this.onStageMouseUp)在舞台外鼠标抬起事件无法通过onStageMouseUp获取,因此需要监听全局的鼠标抬起事件。

import AppConst from "./AppConst";
export default class Rotation extends Laya.Script { lastMouseX:anyisMouseDown:booleanisGameOver:booleanconstructor(){ super(); this.lastMouseX=0;this.isMouseDown=falsethis.isGameOver=false;}/*组件被激活后执行,此时所有节点和组件均已创建完毕,次方法只执行一次*/ onAwake(): void{ //鼠标移出后需要处理为在舞台上抬起鼠标,Laya.stage.on(Laya.Event.MOUSE_OUT,this,this.onStageMouseUp)Laya.stage.on(AppConst.GameOver,this,this.onGameOver);Laya.stage.on(AppConst.RePlayGame,this,this.onRePlayGame)}onRePlayGame():void{this.isGameOver=false;(this.owner as Laya.Sprite3D).transform.localRotationEulerY=0;}onGameOver():void{this.isGameOver=true;this.isMouseDown=false;}onStageMouseDown(e):void{if(this.isGameOver)return;this.lastMouseX=e.stageX;//鼠标在舞台上X轴位置this.isMouseDown=true}onStageMouseMove(e):void{if(this.isGameOver)return;if(this.isMouseDown){var deltaX=e.stageX-this.lastMouseX;(this.owner as Laya.Sprite3D).transform.rotate(new Laya.Vector3(0,deltaX/5,0),true,false);this.lastMouseX=e.stageX;}}onStageMouseUp(e):void{if(this.isGameOver)return;this.lastMouseX=0;this.isMouseDown=false;}}

小球下落以及平台生成

  • Laya提供了角色控制器,Laya.CharacterController 给小球添加角色控制器和碰撞体,通过剩下碰撞来触发穿过,碰到平台,和触发死亡等事件。不建议使用onCollisionEnter和onTriggerExit(other)小球可能同时碰到普通平台或者死亡区域,使用射线碰撞可以避免这个问题。同时为了避免连续掉落后直接碰到死亡区域,在连续通过三次平台后就将平台设置为无敌模式。每通过一个平台积分+1.
import AppConst from "./AppConst";
export default class PlayerController extends Laya.Script3D { constructor(){ super();  }player:Laya.CharacterController;physics:Laya.PhysicsSimulation;ray:Laya.Ray;hitResult:Laya.HitResultisGameOver:boolean;throughCount:number;/*组件被激活后执行,此时所有节点和组件均已创建完毕,次方法只执行一次*/ onAwake(): void{ //获取场景物理模拟器用于射线碰撞this.physics=(this.owner.parent as Laya.Scene3D).physicsSimulation;//创建射线this.ray=new Laya.Ray(new Laya.Vector3(),new Laya.Vector3(0,-1,0));this.hitResult=new Laya.HitResult();//穿件角色控制this.player=this.owner.addComponent(Laya.CharacterController);var collider=new Laya.SphereColliderShape(0.2)this.player.colliderShape=collider;this.player.fallSpeed=50;//下落速度this.player.jumpSpeed=7;//弹回速度this.isGameOver=false;this.throughCount=0;Laya.timer.frameLoop(1,this,this.RayCast)Laya.stage.on(AppConst.RePlayGame,this,this.onRePlayGame)}onRePlayGame():void{(this.owner as Laya.Sprite3D).transform.localPositionY=0;this.isGameOver=false;}RayCast(): void { if(this.isGameOver)return;this.ray.origin=(this.owner as Laya.MeshSprite3D).transform.position;if(this.throughCount>=3&&this.physics.rayCast(this.ray,this.hitResult,0.2)){var col=this.hitResult.collider as Laya.PhysicsColliderthis.setPlatform(col.owner.parent)}if(this.physics.rayCast(this.ray,this.hitResult,0.15)){var col=this.hitResult.collider as Laya.PhysicsColliderif(col.owner.name=="Obstacle"){this.throughCount=0;this.isGameOver=true;console.log("GameOver",col.owner.parent)Laya.stage.event(AppConst.GameOver)return;}else if(col.owner.name=="Bar"){// 碰到平台,播放一个粒子特效Laya.stage.event(AppConst.CreatePrticle,(col.owner as Laya.Sprite3D).transform.position);this.player.jump();this.throughCount=0;}else{col.owner.parent.removeSelf();//回收Laya.Pool.recover("Platform",col.owner.parent)Laya.stage.event(AppConst.MoveCamera,[(col.owner.parent as Laya.Sprite3D).transform.localPositionY,col.owner.parent])//生成一个平台Laya.stage.event(AppConst.CreatePlatform);Laya.stage.event(AppConst.MoveColumn);this.throughCount++;//计算通过个数,生成无敌模式if(this.throughCount>=3){Laya.stage.event(AppConst.AddScore,3);}else{Laya.stage.event(AppConst.AddScore,1);}}}}setPlatform(platform):void{//第一个平台需要特殊处理for(var i=0;i<platform.numChildren;i++){var child=platform.getChildAt(i)child.meshRenderer.material._ColorR=0;child.meshRenderer.material._ColorG=0;child.meshRenderer.material._ColorB=1;if(child.name!="Empty")child.name="Bar";}}
}
  • 小球每穿过一个平台就将当前平台销毁,并在下方重新生成一个平台。用“Bar”代表普通平台,用“Obstacle”代表死亡区域,用“Empty”代表可以穿过的区域
import AppConst from "./AppConst";
export default class SpawnPlatform extends Laya.Script { initPosY:anyspawnCount:anyprefab:Laya.Sprite3Dparent:Laya.Sprite3DplatformArr:Laya.Sprite3D[]=[];constructor(){ super();this.initPosY=-1;this.spawnCount=0}init(prefab,parent):void{this.prefab=prefab;this.parent=parent;this.initPosY=-1;this.spawnCount=0for(var i=0;i<10;i++){this.spawn(prefab,parent)}Laya.stage.on(AppConst.CreatePlatform,this,this.spawn)Laya.stage.on(AppConst.RePlayGame,this,this.onRePlayGame)}onRePlayGame():void{this.platformArr.forEach(element => {if(element.displayedInStage){element.removeSelf();Laya.Pool.recover("Platform",element)}});this.init(this.prefab,this.parent);}spawn(prefab,parent):void{//Laya 对象池var temp=Laya.Pool.getItemByCreateFun("Platform",this.createFun,this)//var temp=Laya.Sprite3D.instantiate(prefab,parent)this.parent.addChild(temp)temp.active=truetemp.transform.localPosition=new Laya.Vector3(0,this.initPosY-(1.5*this.spawnCount),0);this.setPlatform(temp);this.platformArr.push(temp);this.spawnCount++;}//如果对象池没有缓存对象,则调用此方法创建createFun():any{var temp=Laya.Sprite3D.instantiate(this.prefab,this.parent);return temp}//随机设置平台的空位置setPlatform(platform):void{//还原为默认初始状态if(this.spawnCount==0){//第一个平台需要特殊处理for(var i=0;i<platform.numChildren;i++){var child=platform.getChildAt(i)child.getComponent(Laya.PhysicsCollider).isTrigger=false;child.meshRenderer.material._ColorR=0;child.meshRenderer.material._ColorG=0;child.meshRenderer.material._ColorB=0;child.meshRenderer.enable=truechild.name="Bar"if(i==0||i==1){child.name="Empty"child.meshRenderer.enable=false//child.getComponent(Laya.PhysicsCollider).isTrigger=true;}else{child.meshRenderer.enable=true;child.getComponent(Laya.PhysicsCollider).isTrigger=false;}}return;}var emptyCount=0;var obstacleValue=Math.floor(this.getRandom(0,platform.numChildren-1))for(var i=0;i<platform.numChildren;i++){var child=platform.getChildAt(i)//因为有缓存池的存在,因此需要重置child.getComponent(Laya.PhysicsCollider).isTrigger=false;child.meshRenderer.material._ColorR=0;child.meshRenderer.material._ColorG=0;child.meshRenderer.material._ColorB=0;child.meshRenderer.enable=truechild.name="Bar"//控制空位置不超过4个if(obstacleValue!=i&&emptyCount<3&&this.getRandom(0,10)>8){child.meshRenderer.enable=false;//child.getComponent(Laya.PhysicsCollider).isTrigger=true;child.name="Empty";emptyCount++;}else if(obstacleValue==i){child.meshRenderer.material._ColorR=1;child.meshRenderer.material._ColorG=0;child.meshRenderer.material._ColorB=0;child.name="Obstacle"}}if(emptyCount==0){//var child=platform.getChildAt(0)child.name="Empty"child.meshRenderer.material._ColorR=0;child.meshRenderer.material._ColorG=0;child.meshRenderer.material._ColorB=0;child.meshRenderer.enable=false// child.getComponent(Laya.PhysicsCollider).isTrigger=true;}}getRandom(min,max){var delta=max-min;var randomValue=Math.random()*deltareturn min+randomValue}

摄像机跟随与圆柱下移

当小球每次下落之后我们需要将摄像机和圆柱跟随移动,避免穿帮。Laya也很贴心的封装了类似DOTween的功能插件。

//相机移动onCameraFollw(posY,obj):void{Laya.Tween.to( (this.owner as Laya.Camera).transform,{localPositionY:posY},700)}//柱子moveColumn(obj):void{(this.column as Laya.Sprite3D).transform.localPositionY-=1.5;}

Laya源码工程以上传,可下载查看

写在后面的话

对于本身从事Unity开发的人员来说,学习用Laya来做小游戏上手很快,但是也有很多坑需要趟。例如在此demo中,如果采用Laya提供的onUpdate方法中进行射线检测会出现直接穿过平台而不销毁(射线碰撞没有检测到),而采用Laya提供的帧函数方法Laya.timer.frameLoop(1,this,this.RayCast)则不会出现,目测是onUpdate可能没有完全的按照每一帧来刷新,需要学习做更多的项目,熟悉度高了才更快的迭代项目。当量变达到一定程度后也会发生质变。

LayaAir demo 学习3D弹力球简单实现相关推荐

  1. 简单的运动学,用canvas写弹力球

    声明:本文为原创文章,如需转载,请注明来源WAxes,谢谢! 跟之前的随笔一样,因为本人仍是菜鸟一只,所以用到的技术比较简单,不适合大神观看...... 学canvas学了有一个多礼拜了,觉得canv ...

  2. CORE-ESP32C3|eink|日期格式化|IO11解锁|墨水屏操作库|SNTP自动同步|局部刷新|全局刷新|LuatOS-SOC接口|官方demo|学习(12):简单日期显示

    目录 基础资料 探讨重点 参考博文: 实现功能 硬件准备 软件版本 日志及soc下载工具 软件使用 接线示意图 IO11解锁教程可参考: 功能1:基于墨水屏的日期显示: 初始化: 日期显示: 功能2: ...

  3. 基于 HTML5 WebGL 实现的 3D “弹力”布局

    分子力(molecular force),又称分子间作用力.范得瓦耳斯力,是指分子间的相互作用.当二分子相距较远时,主要表现为吸引力,这种力主要来源于一个分子被另一个分子随时间迅速变化的电偶极矩所极化 ...

  4. 如何更高效、系统地学习3D视觉?

    前言 很多粉丝在公众号后台留言,不知如何入门3D视觉.3D领域的主线是什么,一些难点该如何解决,有哪些方法,导师新开的3D视觉方向无人指导等等.这些痛点,工坊的许多童鞋都踩过坑,也为大家提出了许多非常 ...

  5. 如何更高效更系统地学习3D视觉?

    前言 很多粉丝在公众号后台留言,不知如何入门3D视觉.3D领域的主线是什么,一些难点该如何解决,有哪些方法,导师新开的3D视觉方向无人指导等等.这些痛点,工坊的许多童鞋都踩过坑,也为大家提出了许多非常 ...

  6. c语言弹力球小游戏,超级弹力球

    超级弹力球游戏,最休闲的小游戏,游戏玩法比较简单,都是最好玩最实在的玩法模式,用眼睛就可以灵活的进行关卡挑战,玩家需要有一定的实力,毕竟考验操作考验脑力的模式,有能力的玩家快快来,丰富的玩法提供,还有 ...

  7. 学习3D视觉太痛苦了,有哪些高效地学习方法呢?

    前言 很多粉丝在公众号后台留言,不知如何入门3D视觉.3D领域的主线是什么,一些难点该如何解决,有哪些方法,导师新开的3D视觉方向无人指导等等.这些痛点,工坊的许多童鞋都踩过坑,也为大家提出了许多非常 ...

  8. 零基础新手转行学习3D建模你必须知道的市场行情与学习路线

    3D建模如今的市场不如从前,竞争很大,工资非常高,标准非常高,想要胜任一份高薪的工作不是那么容易,只有掌握最新的行情才能更好的了解3D建模,让新手小白了解3D建模市场行情如下: [3D建模学习资料领取 ...

  9. Unity Mesh基础系列(四)mesh变形(制作一个弹力球)

    目录 1 场景搭建 2 Mesh调节器 2.1 准备 2.2 顶点速度 3 Mesh 调节器的输入 3.1 输入检测 3.2 施加力 3.3 力偏移 4 最基础的变形 4.1 将力转换为速度 4.2 ...

最新文章

  1. 1.matlab 中的axis tight,legend
  2. 【BZOJ-2668】交换棋子 最小费用最大流
  3. java args包_Java Args.positive方法代码示例
  4. 风靡硅谷营销界的 MarTech 魔术
  5. 【EOS】EOS环境基础
  6. (时频分析学习)Week01:傅里叶级数,S变换与广义S变换
  7. 基于Android的防疫信息管理系统源码【包调试运行】
  8. windows10睡眠问题完美解决,设置睡眠时间不管用怎么办?
  9. D365几个功能开发思路
  10. 2018年度计划清单
  11. Android程序apk编译、打包、签名
  12. BSP 工程管理实验
  13. 【软考 系统架构设计师】案例分析⑥ Web应用系统架构设计
  14. 手把手教你作者python机器人,自己训练一个机器人助手
  15. oracle 空值排序,排在最前面或者最后面
  16. matlab中diag函数blkdiag函数的用法
  17. 「译」一起探讨 JavaScript 的对象
  18. Batch Normalization(BN)在什么时候用比较合适?
  19. 删除我的电脑中的百度云管家图标
  20. 十八掌全套大数据课程免费送

热门文章

  1. 我的2013之十八年寒窗磨利剑,初出江湖还看今朝
  2. vue实现公告展示效果
  3. 博士申请 | 香港理工大学林婉瑜老师招收人工智能全奖博士/博后/RA
  4. 员工满意度调查表第一部分,对工作回报的满意度25题
  5. SSM+Mysql实现的大学校园兼职系统(功能包含注册登录,发布兼职、个人中心、论坛交流、系统公告、查看兼职信息、查看用户信息、私聊等)
  6. 刘慎权逝世:中国CAD与计算机图形学先驱,川大数学系校友,享年92岁
  7. C++11实现高精度定时器、计时器
  8. 融云 android 推送图标,如何解决 嵌入融云SDK后, Android 手机收不到消息推送?...
  9. Python3爬取豆瓣网站奇幻小说信息
  10. CNN卷积神经网络之RegNet