Laya商业级3d实战_09无缝地图_序列化

laya商业级3d游戏开发

本节目标:无缝随机创建地图
编辑器开发核心思想(unity对象序列化,反序列化)

分析美术资源,建筑物有不同的属性,比如高度
在这里插入图片描述

新建scripts\Game\BuildSpawn.ts

模型层设计

//物体属性模型
export class SpwanItemData {
public constructor() { }
public goName = ‘’;
public length = 0;
}

//生产对象模型
export class SpawnItem {
public spwanItemData: SpwanItemData;
public gob: Laya.Sprite3D;
}

//创建器配置模型
export class SpwanConfigObj {

public constructor() {
}
//资源查找的路径
public findRoot = '';
public spwanItemDatas: SpwanItemData[];
//起始创建值
public startCreateZ = 0;
//创建的长度
public CreateLength = 0;
//回收偏移值,考虑到相机的位置,避免物体在镜头内被回收
public recoverOffset = 0;

}

//建筑物创建组件
export default class BuildSpawn extends Laya.Script {
scene: Laya.Scene;
spwanConfigObj: SpwanConfigObj;
}

Main.ts

     DoExample_RndBuildLoop() {
    let node = new Laya.Node();
    Laya.stage.addChild(node);
    node.addComponent(UnityEnagine)

    SceneManager.LoadSceneByName('Example_RandomBuild', this, (p_Scene3D) => {

        Laya.stage.addChild(p_Scene3D);

        let buildSpawn = new BuildSpawn()
        buildSpawn.scene = p_Scene3D;

        //构造模型实体,参数配置
        buildSpawn.spwanConfigObj = new SpwanConfigObj();
        buildSpawn.spwanConfigObj.CreateLength = 150;
        buildSpawn.spwanConfigObj.findRoot = 'Resources/BuildItem';
        buildSpawn.spwanConfigObj.recoverOffset = -5;
        buildSpawn.spwanConfigObj.startCreateZ = 0;
        buildSpawn.spwanConfigObj.spwanItemDatas = [];
        buildSpawn.spwanConfigObj.spwanItemDatas[0] = new SpwanItemData();
        buildSpawn.spwanConfigObj.spwanItemDatas[0].goName = 'IndustrialWarehouse01'
        buildSpawn.spwanConfigObj.spwanItemDatas[0].length = 18;
        //假如物体的增加减少或者属性发生变化,都要进行代码修改
        //物体数量很多或者业务更为复杂时,编码较为繁琐
        //而且策划也无法进行配置
        //把模块封装为数据驱动模式以解决上述的问题
        //可利用u3d可视化编辑优势,进行序列化和反序列化
        node.addComponentIntance(buildSpawn);
    });
}

Unity3d序列化模型
在u3d环境中操作
新建脚本BuildSpawn.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System;
using Newtonsoft.Json;

[System.Serializable]
public class SpwanItemData
{
public string goName ;
public int length ;
}

[System.Serializable]
public class SpwanConfigObj
{

public string findRoot = "";

public SpwanItemData[] spwanItemDatas;

//起始创建值
public float startCreateZ = 0;
//创建的长度
[SerializeField]
public float CreateLength;

//回收偏移值,考虑到相机的位置,避免物体在镜头内被回收
public float recoverOffset = -5;

}

public class SpawnItem
{

public SpwanItemData spwanItemData;
public GameObject gob;

}

public class BuildSpawn : MonoBehaviour
{

[SerializeField]
protected SpwanConfigObj cfg = new SpwanConfigObj();

[ContextMenu("LogAndCopyJson")]
void LogAndCopyJson()
{
    var jsondata = Newtonsoft.Json.JsonConvert.SerializeObject(this.cfg);
    UnityEngine.GUIUtility.systemCopyBuffer = jsondata;
    Debug.Log("已经复制到粘帖板");
    Debug.Log(jsondata);
}

}

经过一番调整,最终得到以下数据
Resources/BuildItem

在这里插入图片描述

拷贝序列化信息到黏贴板
在这里插入图片描述
在这里插入图片描述

通过json序列化之后,得到一串字符串

把字符串转换成实体

对象反序列化
Mian.ts
新建方法
example_DeserializeObject() {
let buildSpawn = new BuildSpawn()
let bulidjsonStr = ‘{“findRoot”:“Resources/BuildItem”,“spwanItemDatas”:[{“goName”:“IndustrialWarehouse01”,“length”:18.0},{“goName”:“IndustrialWarehouse03”,“length”:27.0}],“startCreateZ”:0.0,“CreateLength”:150.0,“recoverOffset”:-10.0}’;
buildSpawn.spwanConfigObj = JSON.parse(bulidjsonStr);
console.log(buildSpawn);
}

onConfigLoaded(): void {
//this.example_spwan()
this.example_DeserializeObject()
}

F8 f5
成员成功反序列化后拥有了定义好的属性

在这里插入图片描述

这个时候,策划对地图有更多的需求和想法,把编辑器交给策划,前端只需要场景和导出数据即可

建筑物的具体实现

由于用到的了伪随机算法和类似C#字典数据结构等,导入类库
LayaIde\framework\JFrameWork到src\scripts

GameSample.ts到src\scripts

在这里插入图片描述

export default class BuildSpawn extends Laya.Script {

spwanConfigObj: SpwanConfigObj;
runtimeItems: SpawnItem[] = [];
//返回首页时清理对象池用到
poolsignMap: Dictionary<string, string> = new Dictionary();
currentZ = 0;
startCreateZ = 0;
scene: Laya.Scene3D;
seed: SeedRnd = new SeedRnd(0);
onStart() {
    this.startCreateZ = this.spwanConfigObj.startCreateZ;
    this.Create2End();
}

protected endZ(): number {
    return this.currentZ + this.spwanConfigObj.CreateLength;
}

Create2End() {
    var p_endZ = this.endZ();
    while (this.startCreateZ < p_endZ) {
        let spawnItem = this.SpawnItem(this.startCreateZ);
        this.startCreateZ += spawnItem.spwanItemData.length;
    }
}

protected SpawnItem(z: number): SpawnItem {

    let rndIdx = this.seed.getRandomInt_NotIncludeMax(0, this.spwanConfigObj.spwanItemDatas.length);
    //随机数据
    var spwanItemData = this.spwanConfigObj.spwanItemDatas[rndIdx];
    //对象池创建
    var item = Laya.Pool.getItemByCreateFun(spwanItemData.goName, () => { return this.CreateSpwanItem(spwanItemData); }, this) as SpawnItem;
    this.scene.addChild(item.gob);
    this.onSpawn(item.gob, spwanItemData, z);
    return item;
}

//对应上一章的CreateItem,返回固定长度改为返回SpwanItemData,该数据模型就包含了物体的长度信息
protected CreateSpwanItem(spwanItemData: SpwanItemData): SpawnItem {
var gob = GameObject.Find<Laya.Sprite3D>(this.scene, this.spwanConfigObj.findRoot + ‘/’ + spwanItemData.goName);
var newGo = Laya.Sprite3D.instantiate(gob);
this.scene.addChildren(newGo);
newGo.active = true;

    let spawnItem = new SpawnItem();
    spawnItem.gob = newGo;
    spawnItem.spwanItemData = spwanItemData;
    //给回收用的
    this.runtimeItems.push(spawnItem);
    //返回首页时清理对象池用到
    this.poolsignMap.add(spwanItemData.goName, 'poolsign');
    return spawnItem;
}

//置创建物体创建的属性,
protected onSpawn(newGo: Laya.Sprite3D, spwanItemData: SpwanItemData, z) {
newGo.transform.position = new Laya.Vector3(0, 0, z);
var scale = newGo.transform.localScale;
var arryNum = [-1, 1];
this.seed.getRandomIntArry(arryNum);
//物体镜像 x=1 x=-1,构成建筑物朝向,文章中见图1
newGo.transform.localScale = scale;
}

onUpdate() {

    this.Create2End();
    this.recoverLessZ();

}
//回收超出镜头的物体
recoverLessZ() {
    for (const spwanitem of this.runtimeItems) {
        if (spwanitem.gob.displayedInStage) {
            let length = spwanitem.spwanItemData.length;
            if (spwanitem.gob.transform.position.z + length * 0.5 < this.currentZ + this.spwanConfigObj.recoverOffset) {
                Laya.Pool.recover(spwanitem.spwanItemData.goName, spwanitem);
                spwanitem.gob.removeSelf();

            }
        }
    }
}

//返回首页会删除对象池实例化的资源,所以要清空对象池,避免游戏开始时取到空对象
onDestroy() {
for (const iterator of this.poolsignMap.keys) {
Laya.Pool.clearBySign(iterator)
}
}

}

主干部分
this.Create2End();
this.recoverLessZ();
//从DoSpawnItem开始 演示流程

图1,物体镜像
在这里插入图片描述

回到
Main.ts
新建测试方法
example_BuildSpawn() {
let node = new Laya.Node();
Laya.stage.addChild(node);
node.addComponent(UnityEnagine)

    SceneManager.LoadSceneByName('Example-buildloop', this, (p_Scene3D) => {

        Laya.stage.addChild(p_Scene3D);

        let buildSpawn = new BuildSpawn()
        let bulidjsonStr = '{"findRoot":"Resources/BuildItem","spwanItemDatas":[{"goName":"IndustrialWarehouse01","length":18.0},{"goName":"IndustrialWarehouse03","length":27.0}],"startCreateZ":0.0,"CreateLength":150.0,"recoverOffset":-10.0}';
        buildSpawn.spwanConfigObj = JSON.parse(bulidjsonStr);
        buildSpawn.scene = p_Scene3D;

        //硬编码
        //buildSpawn.spwanConfigObj = new SpwanConfigObj();
        //buildSpawn.spwanConfigObj.CreateLength = 150;
        //buildSpawn.spwanConfigObj.findRoot = 'Resources/BuildItem';
        //buildSpawn.spwanConfigObj.recoverOffset = -5;
        //buildSpawn.spwanConfigObj.startCreateZ = 0;
        //buildSpawn.spwanConfigObj.spwanItemDatas = [];
        //buildSpawn.spwanConfigObj.spwanItemDatas[0] = new SpwanItemData();
        //buildSpawn.spwanConfigObj.spwanItemDatas[0].goName = 'IndustrialWarehouse01'
        //buildSpawn.spwanConfigObj.spwanItemDatas[0].length = 18;
        //buildSpawn.spwanConfigObj.spwanItemDatas[1] = new SpwanItemData();
        //buildSpawn.spwanConfigObj.spwanItemDatas[1].goName = 'IndustrialWarehouse03'
        //buildSpawn.spwanConfigObj.spwanItemDatas[1].length = 27;

        node.addComponentIntance(buildSpawn);
    });
}

F8编译 f5 运行
在这里插入图片描述

在这里插入图片描述

对比硬编码和数据序列化方式

美术或者地图数据一但更新,硬编码方式就要重新写一遍,且地图编辑策划也无法同步进行
采用数据反序列化方式,只需重新导入3d资源,更新bulidjsonStr 的字符串即可

什么时候采用哪种实现方式,视具体情况而定
变化频繁的模块就用数据封装变化
需求明确可以直接写死

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程之力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值