LayaAir demo 学习3D弹力球简单实现

公司要开始做小游戏了,经过研究讨论之后决定采用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 
  {
      //加载完成获取到了Scene3d
      Laya.stage.addChildAt(scene,0);
      this.scene=scene;
            //加载完成获取到了Scene3d
      Laya.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=false
      this.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:any
  isMouseDown:boolean
  isGameOver:boolean
  constructor(){ 
      super(); 
      this.lastMouseX=0;
      this.isMouseDown=false
      this.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.HitResult
  isGameOver: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.PhysicsCollider
      this.setPlatform(col.owner.parent)
    }
    if(this.physics.rayCast(this.ray,this.hitResult,0.15))
    {
      var col=this.hitResult.collider as Laya.PhysicsCollider

      if(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:any
    spawnCount:any
    prefab:Laya.Sprite3D
    parent:Laya.Sprite3D
    platformArr: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=0
        for(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=true
       temp.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=true
                child.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=true
            child.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()*delta
        return 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;
  }

Demo效果示意

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

写在后面的话

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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值