three.js的ECS框架

最近webgl比较流行,three.js又是一个比较知名的开源库,作为一个unity开发我也来学习学习大笑

然后,提供一种Unity这样的实体-组件-系统的开发方式。

框架代码如下:(我取名叫ThreeECS)


/// <reference path="three.js" />

// 基于three.js的ECS框架
// yangxun
//==================ESC驱动系统=========================
function ThreeECS() {
    this.scene = new THREE.Scene();
    //// this.scene.background = new THREE.Color(0,0,0,0);
    this.renderer = new THREE.WebGLRenderer({
        canvas: document.getElementById('canvas'),
        alpha: true
    });
    this.renderer.setClearAlpha(0)
    this.renderer.antialias = true;
    this.renderer.setPixelRatio(window.devicePixelRatio);
    //this.renderer.gammaOutput = true;
    this.isInited = false;
}
var preCallbacks = new Array();
var updateCallbacks = new Array();
var nextCallbacks = new Array();
function Loop() {
    requestAnimationFrame(Loop);
    for (var i = 0; i < preCallbacks.length; i++) {
        preCallbacks[i]();
    }
    preCallbacks.length = 0;
    for (var i = 0; i < updateCallbacks.length; i++) {
        updateCallbacks[i]();
    }
    for (var i = 0; i < nextCallbacks.length; i++) {
        nextCallbacks[i]();
    }
    nextCallbacks.length = 0;
    YxTime.frames++;
}
function AddToHtml() {
    if (this.isInited) {
        return;
    }
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(this.renderer.domElement);
    ThreeECS.Instance = this;
    this.gameObjects = new Array();
    YxInput.Init();
    this.isInited = true;
    Loop();
    window.addEventListener('resize', function () {
        if (YxCamera.HasCamera()) {
            for (var i = 0; i < YxCamera.array.length; i++) {
                var cam = YxCamera.array[i].threeObj;
                cam.aspect = window.innerWidth / window.innerHeight;
                cam.updateProjectionMatrix();
            }
        }
        ThreeECS.Instance.renderer.setSize(window.innerWidth, window.innerHeight);
    }, false);
    function render() {
        ThreeECS.Instance.renderer.clear();
        if (YxCamera.HasCamera()) {
            for (var i = 0; i < YxCamera.array.length; i++) {
                var cam = YxCamera.array[i];
                ThreeECS.Instance.renderer.render(ThreeECS.Instance.scene, YxCamera.array[i].threeObj);
            }
        }
        for (var i = 0; i < ThreeECS.Instance.gameObjects.length; i++) {
            var g = ThreeECS.Instance.gameObjects[i];
            if (g.active === true && !g.isDisposed) {
                for (var j = 0; j < g.component.length; j++) {
                    var c = g.component[j];
                    if (c.Started === false) {
                        c.Started = true;
                        if (c.Start != null) {
                            c.Start();
                        }
                    }
                }
            }
        }
        for (var i = 0; i < ThreeECS.Instance.gameObjects.length; i++) {
            var g = ThreeECS.Instance.gameObjects[i];
            if (g.active === true && !g.isDisposed) {
                for (var j = 0; j < g.component.length; j++) {
                    var c = g.component[j];
                    if (c.Started && c.Update != null) {
                        c.Update();
                    }
                }
            }
        }
        for (var i = ThreeECS.Instance.gameObjects.length - 1; i >= 0; i--) {
            var n = ThreeECS.Instance.gameObjects[i];
            if (n === null || n.isDisposed) {
                ThreeECS.Instance.gameObjects.splice(i, 1);
            }
        }
        for (var i = 0; i < ThreeECS.Instance.gameObjects.length; i++) {
            var g = ThreeECS.Instance.gameObjects[i];
            if (g.active === true) {
                for (var j = 0; j < g.component.length; j++) {
                    var c = g.component[j];
                    if (c.Started && c.LateUpdate != null) {
                        c.LateUpdate();
                    }
                }
            }
        }
    }
    updateCallbacks.push(render);
}
Object.assign(ThreeECS.prototype, {
    AddToHtml: AddToHtml
});
//==========GameObject实体,ECS中的基本类型===============
function YxGameObject(name, threeObj) {
    this.name = name;
    this.threeObj = threeObj;
    if (this.threeObj != null) {
        this.threeObj.name = name;
        this.threeObj.yxproxy = this;
    }
    
    this.active = true;
    this.isDisposed = false;
    this.component = new Array();
    if (ThreeECS.Instance === null) {
        console.log("ThreeECS还没有附加到html");
    } else {
        ThreeECS.Instance.gameObjects.push(this);
        if (this.threeObj != null) {
            ThreeECS.Instance.scene.add(this.threeObj);
            this.active = true;
        }
    }
    this.SetActive = function (enable) {
        if (this.active === enable) {
            return;
        }
        this.active = enable;
        if (enable) {
            ThreeECS.Instance.scene.add(this.threeObj);
        } else {
            ThreeECS.Instance.scene.remove(this.threeObj);
        }
    }
    this.AddComponent = function (com) {
        if (com != null) {
            this.component.push(com);
            com.gameObject = this;
            if (com.Awake != null) {
                com.Awake();
            }
            com.Started = false;
            return com;
        }
        
    }
    this.GetComponentById = function(yxid){
        for (var i = 0; i < this.component.length; i++) {
            if (this.component[i].yxid === yxid) {
                return this.component[i];
            }
        }
    }
    this.RemoveComponent = function (com) {
        if (com != null) {
            this.component.splice(com);
            if (com.OnDestroy != null) {
                com.OnDestroy();
            }
        }
        
    }
    this.Destroy = function () {
        if (this.isDisposed) {
            console.log(this.name + "对象已经被释放");
            return;
        }
        if (this.threeObj != null && this.active) {
            ThreeECS.Instance.scene.remove(this.threeObj);
        }
        for (var i = 0; i < this.component.length; i++) {
            var c = this.component[i];
            if (c != null && c.OnDestroy != null) {
                c.OnDestroy();
                c = null;
            }
        }
        if (this.threeObj != null) {
            this.threeObj = null;
        }
        this.component = null;
        this.isDisposed = true;
    }
}
YxGameObject.Find = function (name) {
    var gos = ThreeECS.Instance.gameObjects;
    for (var i = 0; i < gos.length; i++) {
        if (gos[i].name === name) {
            return gos[i];
        }
    }
}
YxGameObject.GetProxy = function (threeObj) {
    var p = threeObj;
    do {
        if (p.yxproxy != undefined && p.yxproxy != null) {
            return p.yxproxy;
        }
        p = p.parent;
    } while (p != undefined && p != null)
    return null;
}
//==========Camera实体,继承自YxCameObject================
function YxCamera(name,camera, depth) {
    YxGameObject.call(this);
    this.name = name;
    this.threeObj = camera;
    this.depth = depth;
    YxCamera.array.push(this);
    YxCamera.array.sort(function (a, b) { return a.depth <= b.depth; });
    //渲染范围,暂未实现
    var camRect;
    this.SetRect = function (rect) {
        camRect = rect;
    };
    this.GetRect = function () {
        return camRect;
    }
    this.Destroy = function () {
        this.threeObj.dispose();
        this.threeObj = null;
        YxCamera.array.splice(this);
        YxCamera.array.sort(function (a, b) { return a.depth <= b.depth; });
    }
}
(
function () {
    var Super = function () { };
    Super.prototype = YxGameObject.prototype;
    YxCamera.prototype = new Super();
    YxCamera.prototype.constructor = YxCamera;
}
)();
YxCamera.array = new Array();
YxCamera.HasCamera = function(){
    return YxCamera.array.length > 0; 
}
YxCamera.Find = function (name) {
    var cams = YxCamera.array;
    for (var i = 0; i < cams.length; i++) {
        if (cams[i].name === name) {
            return cams[i];
        }
    }
}
//===============动画组件================================
function YxAnimation() {
    var curr = null;
    var idx = -1;
    var playing = true;
    var animations = null;
    var raw = null;
    var clock = new THREE.Clock();
    this.Awake = function () {
        raw = this.gameObject.threeObj;
        animations = this.gameObject.threeObj.animations
        raw.mixer = new THREE.AnimationMixer(raw);
    }
    this.Update = function () {
        if (curr != null && playing) {
            raw.mixer.update(clock.getDelta());
        }
    }
    this.Play = function (name) {
        for (var i = 0; i < animations.length; i++) {
            if (animations[i].name === name) {
                var action = this.gameObject.threeObj.mixer.clipAction(animations[i]);
                if (curr != null) {
                    curr.stop();
                }
                curr = action;
                idx = i;
                playing = true;
                action.play();
            }
        }
    }
    this.PlayIndex = function (i) {
        var action = this.gameObject.threeObj.mixer.clipAction(animations[i]);
        if (curr != null) {
            curr.stop();
        }
        curr = action;
        idx = i;
        playing = true;
        action.play();
    }
    this.GetCurrentClip = function () {
        return curr;
    }
    this.Pause = function () {
        playing = false;
    }
    this.Stop = function () {
        curr = null;
        playing = false;
    }
    this.IsPlayingIndex = function (i) {
        return idx === i && playing;
    }
    this.IsPlayingName = function (name) {
        return curr.name === name && playing
    }
}
//===============输入系统================================
// 码状态 0表示未按下,1表示按下
function KeyState(keycode) {
    this.key = keycode;
    this.preState = 0;
    this.state = 0
}
function YxInput() {
}
YxInput.mouse = new THREE.Vector2();
var inputEvents = new Array();
var GetKeyState = function(key){
    for (var i = 0; i < inputEvents.length; i++) {
        if (inputEvents[i].key === key) { return inputEvents[i]; }
    }
    return null;
}
var SetKeyState = function (key,state) {
    var keystate = GetKeyState(key);
    if (keystate == null) {
        keystate = new KeyState(key);
        inputEvents.push(keystate);
    }
    keystate.preState = keystate.state;
    keystate.state = state;
    return keystate;
}

YxInput.Init = function () {
    /// <summary>
    /// 输入系统的初始化,注册一些系统回调
    /// </summary>

    document.onkeydown = function (e) {
        var keynum = window.event ? e.keyCode : e.which;
        var keychar = String.fromCharCode(keynum);
        var state = SetKeyState(keychar, 1);
        nextCallbacks.push(function () {
            state.preState = state.state;
        });
    }
    document.onkeyup = function (e) {
        var keynum = window.event ? e.keyCode : e.which;
        var keychar = String.fromCharCode(keynum);
        var state = SetKeyState(keychar, 0);
        nextCallbacks.push(function () {
            state.preState = state.state;
        });
    }
    document.onmousedown = function (e) {
        var idx = e.button;
        var state = SetKeyState('mouse' + idx, 1);
        nextCallbacks.push(function () {
            state.preState = state.state;
        });
    }
    document.onmouseup = function (e) {
        var idx = e.button;
        var state = SetKeyState('mouse' + idx, 0);
        nextCallbacks.push(function () {
            state.preState = state.state;
        });
    }
    document.onmousemove = function (e) {
        YxInput.mouse.set((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);
    }
    ThreeECS.Instance.renderer.domElement.addEventListener('touchstart', function (e) {
        e.preventDefault();
        var t0 = e.targetTouches[0];
        YxInput.mouse.set((t0.pageX / window.innerWidth) * 2 - 1, -(t0.pageY / window.innerHeight) * 2 + 1);
        var idx = 0;
        var state = SetKeyState('mouse' + idx, 1);
        nextCallbacks.push(function () {
            state.preState = state.state;
        });
    });
    ThreeECS.Instance.renderer.domElement.addEventListener('touchend', function (e) {
        var t0 = e.changedTouches[0];
        YxInput.mouse.set((t0.pageX / window.innerWidth) * 2 - 1, -(t0.pageY / window.innerHeight) * 2 + 1);
        var idx = 0;
        var state = SetKeyState('mouse' + idx, 0);
        nextCallbacks.push(function () {
            state.preState = state.state;
        });
    });
}

YxInput.GetKeyDown = function (key) {
    var state = GetKeyState(key);
    if (state == null) {
        return false;
    } else {
        return state.preState == 0 && state.state == 1;
    }
}
YxInput.GetKey = function(key){
    var state = GetKeyState(key);
    if (state == null) {
        return false;
    } else {
        return state.preState == 1 && state.state == 1;
    }
}
YxInput.GetKeyUp = function (key) {
    var state = GetKeyState(key);
    if (state == null) {
        return false;
    } else {
        return state.preState == 1 && state.state == 0;
    }
}
YxInput.GetMouseButtonDown = function (index) {
    var state = GetKeyState('mouse'+index);
    if (state == null) {
        return false;
    } else {
        return state.preState == 0 && state.state == 1;
    }
}
YxInput.GetMouseButton = function (index) {
    var state = GetKeyState('mouse' + index);
    if (state == null) {
        return false;
    } else {
        return state.preState == 1 && state.state == 1;
    }
}
YxInput.GetMouseButtonUp = function (index) {
    var state = GetKeyState('mouse' + index);
    if (state == null) {
        return false;
    } else {
        return state.preState == 1 && state.state == 0;
    }
}
//================协程=================================
function YxCoroutine() {
    /// <summary>
    /// 协程实现,类似于unity的意义的协程,没有采用ES6特性
    /// </summary>

    var stack = new Array();
    this.index = -1;

    this.AddTask = function (func) {
        /// <summary>
        /// 增加任务
        /// </summary>
        /// <param name="func">待执行方法</param>

        stack.push(func);
        return this;
    };

    this.Yield = function (y) {
        /// <summary>
        /// 增加一个中断
        /// </summary>
        /// <param name="y">包含IsDone()方法的对象</param>

        stack.push(y);
        return this;
    };

    this.Current = function () {
        if (this.index > stack.length - 1) {
            return null;
        }
        return stack[this.index];
    };
    this.MoveNext = function () {
        while (this.index < stack.length - 1) {
            this.index += 1;
            var curr = this.Current();
            if (curr.hasOwnProperty('IsDone')) {
                return true;
            }
            curr();
        }
        return false;
    };
    this.Reset = function () {
        this.index = -1;
    };
    this.IsCompleted = function () {
        return this.index >= stack.length - 1
    };
    this.Clear = function () {
        stack.length = 0;
        this.index = -1;
    }
}

function YxWaitForEndOfFrame() {
    /// <summary>
    /// 等待一帧
    /// </summary>

    var startFrame = null;

    this.IsDone = function () {
        if (startFrame === null) {
            startFrame = YxTime.GetTotalFrame();
        }
        return YxTime.GetTotalFrame() - startFrame > 0;
    }
}

function YxWaitForSeconds(sec) {
    /// <summary>
    /// 等待指定秒
    /// </summary>
    /// <param name="sec">秒,float</param>

    var waitMS = sec;
    var timeStart = null;

    this.IsDone = function () {
        if (timeStart === null) {
            timeStart = YxTime.GetSecondsSinceStart();
        }
        var r = YxTime.GetSecondsSinceStart() - timeStart > waitMS;
        return r;
    }
}

function YxCoroutineInvoker() {
    /// <summary>
    /// 协程驱动器
    /// </summary>

    var cors = new Array();

    this.StartCoroutine = function (cor) {
        /// <summary>
        /// 启动协程
        /// </summary>

        if (!YxArray.Contain(cors, cor)) {
            cor.MoveNext();
            cors.push(cor);
        }
    };
    this.StopCoroutine = function (cor) {
        /// <summary>
        /// 停止协程
        /// </summary>

        var idx = YxArray.IndexOf(cors, cor);
        if (idx >= 0) {
            cors.splice(idx,1);
        }
    };

    this.Update = function () {
        for (var i = 0; i < cors.length; i++) {
            var cor = cors[i];
            if (!cor.IsCompleted()) {
                var y = cor.Current();
                while (y != null && y.IsDone() && cor.MoveNext()) {
                    y = cor.Current();
                }
            }
        }
        for (var i = cors.length - 1; i >= 0 ; i--) {
            if (cors[i].IsCompleted()) {
                cors.splice(i, 1);
            }
        }
    };
}
var yxCoroutineInvoker = null;
YxCoroutineInvoker.Instance = function () {
    if (yxCoroutineInvoker != null) {
        return yxCoroutineInvoker;
    } else {
        var invokerObj = new YxGameObject('Yx_Built-in_YxCoroutineInvoker', null);
        yxCoroutineInvoker = new YxCoroutineInvoker();
        invokerObj.AddComponent(yxCoroutineInvoker);
        return yxCoroutineInvoker;
    }
}
YxCoroutineInvoker.StartCoroutine = function (cor) {
    YxCoroutineInvoker.Instance().StartCoroutine(cor);
};
YxCoroutineInvoker.StopCoroutine = function (cor) {
    YxCoroutineInvoker.Instance().StopCoroutine(cor);
};
//==========================时间===========================
function YxTime() { }
var startTime = (new Date()).valueOf();
YxTime.GetSecondsSinceStart = function () {
    return ((new Date()).valueOf() - startTime) / 1000.00;
}
YxTime.frames = 0;
YxTime.GetTotalFrame = function(){
    return YxTime.frames;
}
//========================数组扩展==========================
var YxArray = {};
YxArray.Contain = function (array, v) {
    for (var i = 0; i < array.length; i++) {
        if (array[i] === v) {
            return true;
        }
    }
    return false;
}
YxArray.IndexOf = function (array, v) {
    for (var i = 0; i < array.length; i++) {
        if (array[i] === v) {
            return i;
        }
    }
    return -1;
}
YxArray.Remove = function (array,v) {
    for (var i = 0; i < array.length; i++) {
        if (array[i] === v) {
            array.splice(i, 1);
            return true;
        }
    }
    return false;
}
//=========================================================


那,使用上就像下面这样:(js代码加入html标签里这些就不提了)main.js
//入口
function Main(){
    // 初始化ECS
    var ECS = new ThreeECS();
    ECS.AddToHtml();
    var logic = new YxGameObject("logic", null);
    function logicObject() {
        // 新增相机
        var rawCam = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 6000);
        var cam = new YxCamera("相机1", rawCam, 0);
        // 演示查找功能,找到相机并设置位置
        var findCam = YxCamera.Find("相机1");
        findCam.threeObj.position.set(0, 80, 30);
        // 演示组件系统,增加相机控制组件
        cam.AddComponent(new CameraController(cam));
        // 加一个平面
        var g0 = new THREE.PlaneGeometry(10000, 10000, 1, 1);
        var m0 = new THREE.MeshLambertMaterial({ color: 0xffffff });
        m0.map = THREE.ImageUtils.loadTexture("./tex/terrain.jpg");
        // 设置平铺
        g0.faceVertexUvs[0] = [];
        var t0 = [new THREE.Vector2(0, 100), new THREE.Vector2(0, 0), new THREE.Vector2(100, 100)];
        var t1 = [new THREE.Vector2(0, 0), new THREE.Vector2(100, 0), new THREE.Vector2(100, 100)];
        g0.faceVertexUvs[0] = [t0, t1];
        m0.map.wrapS = THREE.RepeatWrapping;
        m0.map.wrapT = THREE.RepeatWrapping;
        //
        var yxg0 = new YxGameObject("terrain", new THREE.Mesh(g0, m0));
        yxg0.threeObj.rotation.set(-0.5 * Math.PI, 0, 0);
        // 加一个cube
        var geometry = new THREE.CubeGeometry(5, 5, 5);
        var material = new THREE.MeshLambertMaterial({ color: 0xcc0033 });
        var rawGO = new THREE.Mesh(geometry, material);
        var go = new YxGameObject("testModel", rawGO);
        go.threeObj.position.set(0, 3, 0);
        go.AddComponent(new Rotate());
        // 演示输入组件,点鼠标中键变色
        go.AddComponent(new RandomColor());
        //go.SetActive(false);
        // 加一个环境光
        var rawAmbient = new THREE.AmbientLight(0xa0a0a0);
        var ambient = new YxGameObject("环境光", rawAmbient);
        // 加一个方向光
        var rawDL = new THREE.DirectionalLight(0x777777, 1)
        var DL = new YxGameObject("方向光", rawDL);
        // 演示查找功能,查找到方向光,并设置位置
        var findDL = YxGameObject.Find("方向光");
        findDL.threeObj.position.set(0, 0, 1);
        // 演示加载一个fbx
        var fbxloader = new THREE.FBXLoader();
        fbxloader.load("./models/xsi_man_skinning.fbx", function (raw) {
            var fbx = new YxGameObject("fbx1", raw);
            fbx.threeObj.position.set(20, -10, 0);
            fbx.threeObj.scale.set(1, 1, 1);
            // 演示动画组件功能
            var anim = fbx.AddComponent(new YxAnimation());
            anim.Play("Take 001");
            fbx.SetActive(true);
            // 演示逻辑 绕圈组件
            fbx.AddComponent(new RotateAround(new THREE.Vector3(0, 0, 0), 20));
        },
        function () { console.log("progress");},
        function (error) { console.log(error); });
        // 演示加载天空盒
        var path = "./tex/cube/sky1/";//设置路径
        var directions = ["px", "nx", "py", "ny", "pz", "nz"];//获取对象
        var format = ".png";//格式
        var skyGeometry = new THREE.BoxGeometry(5000, 5000, 5000);
        var materialArray = [];
        for (var i = 0; i < 6; i++)
            materialArray.push(new THREE.MeshBasicMaterial({
                map: THREE.ImageUtils.loadTexture(path + directions[i] + format),
                side: THREE.BackSide
            }));
        var skyMaterial = new THREE.MeshFaceMaterial(materialArray);
        var skyBox = new THREE.Mesh(skyGeometry, skyMaterial);
        new YxGameObject("天空盒", skyBox);
        // 演示,点击地面人跑过去
        fbxloader.load("./models/xsi_man_skinning.fbx", function (raw) {
            var fbx2 = new YxGameObject("fbx2", raw);
            fbx2.threeObj.position.set(30, 0, 0);
            fbx2.threeObj.scale.set(1, 1, 1);
            var anim2 = fbx2.AddComponent(new YxAnimation());
            anim2.Play("Take 001");
            fbx2.AddComponent(new CharacterController());
        },
        function () { console.log("progress"); },
        function (error) { console.log(error); });
    };
    logicInstacne = new logicObject();

    logic.AddComponent(logicInstacne);
}
window.onload = Main;

其中,功能以组件的形式提供。

例如,一个点击变色功能:RandomColor.js

// 演示功能
// 按下鼠标中键,随机改变实体颜色
function RandomColor() {
    //var getRandomColor = function () {
    //    return '0x' +
    //      (function (color) {
    //          return (color += '0123456789abcdef'[Math.floor(Math.random() * 16)])
    //            && (color.length == 6) ? color : arguments.callee(color);
    //      })('');
    //}
    this.Update = function () {
        if (YxInput.GetMouseButtonDown(1)) {
            var r = Math.random();
            var g = Math.random();
            var b = Math.random();
            this.gameObject.threeObj.material.color.setRGB(r,g,b);
        } else {
            //console.log('没有按下a');
        }
    }
}

点击移动功能:CharacterController.js

// 点击地面,人走过去
function CharacterController() {
    var camera = YxCamera.Find("相机1").threeObj;
    var point;
    this.Start = function () {
        point = this.gameObject.threeObj.position;
    }
    this.Update = function () {
        if (YxInput.GetMouseButtonDown(0)) {
            var go = this.gameObject.threeObj;
            var x = YxInput.mouse.x;
            var y = YxInput.mouse.y;
            var vector = new THREE.Vector3(x, y, 0.5).unproject(camera);
            var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());

            var intersects = raycaster.intersectObjects(ThreeECS.Instance.scene.children,true);

            if (intersects.length > 0) {
                for (var i = 0; i < intersects.length; i++) {
                    var o = intersects[i].object;
                    if (o.name == "terrain") {
                        point = intersects[i].point;
                        break;
                    }
                }
            }
        }
        var pos = this.gameObject.threeObj.position;
        if (pos.distanceTo(point) > 0.3) {
            var p = new THREE.Vector3(point.x, point.y, point.z);
            var dir = (p.sub(pos)).normalize();
            var e = Math.asin(dir.x);
            if (dir.z<0) {
                e = Math.PI - e;
            }
            this.gameObject.threeObj.rotation.set(0, e, 0);
            pos.add(dir.multiplyScalar(0.4));
        }
    }
}

这些功能组件都是在main.js中加到对应的实体上面去的,可以参照上面的代码。

html比较简单:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <style>
        canvas {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <script src="./com/three.js"></script>
    <script src="./com/FBXLoader.js"></script>
    <script src="./com/GLTFLoader.js"></script>
    <script src="./com/OrbitControls.js"></script>
    <script src="./com/ThreeECS.js"></script>
    <script src="./logic/import.js"></script>
</body>
</html>


import.js,把用到的js代码加入网页。

var array = new Array();
// logic
array.push("./logic/CameraController.js");
array.push("./logic/Rotate.js");
array.push("./logic/RandomColor.js");
array.push("./logic/RotateAround.js");
array.push("./logic/CharacterController.js");
array.push("./logic/main.js");

for (var i = 0; i < array.length; i++) {
    new_element=document.createElement("script");
    new_element.setAttribute("src",array[i]);
    document.body.appendChild(new_element);
}

通过上面这些可以看到,通过这样的方式组织代码,一些低层次的细节在逻辑开发时就不需要关心了,比如场景管理,渲染,输入事件。当然,这个是仓促之作,也并不完善,仅代表一种思路。

猜你喜欢

转载自blog.csdn.net/yangxun983323204/article/details/79127233