刘忠健 před 1 rokem
rodič
revize
392615b5d3

+ 15 - 0
flappy/.vscode/launch.json

@@ -0,0 +1,15 @@
+{
+    // 使用 IntelliSense 了解相关属性。 
+    // 悬停以查看现有属性的描述。
+    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "chrome",
+            "request": "launch",
+            "name": "针对 localhost 启动 Chrome",
+            "url": "http://localhost:18080",
+            "webRoot": "${workspaceFolder}"
+        }
+    ]
+}

+ 586 - 0
flappy/hilo/hilo-flash.js

@@ -0,0 +1,586 @@
+;(function(){
+
+/**
+ * @class Flash渲染器。将可视对象以flash方式渲染出来。
+ * @augments Renderer
+ * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。
+ * @module hilo/flash/FlashRenderer
+ * @requires hilo/core/Class
+ * @requires hilo/core/Hilo
+ * @requires hilo/renderer/Renderer
+ */
+var FlashRenderer = (function(){
+
+var _stageStateList = ["x", "y", "scaleX", "scaleY", "rotation", "visible", "alpha"];
+var _stateList = _stageStateList.concat(["pivotX", "pivotY", "width", "height", "depth"]);
+var _textStateList = _stateList.concat(["text", "color", "textAlign", "outline", "lineSpacing", "font"]);
+var n = 0;
+
+var state = {
+    View: _stateList,
+    Stage: _stageStateList,
+    Graphics: _stateList,
+    Text: _textStateList
+};
+
+function createFid(target){
+    return target.id + (n++);
+}
+
+return Hilo.Class.create(/** @lends FlashRenderer.prototype */{
+    Extends: Hilo.Renderer,
+    constructor: function(properties){
+        FlashRenderer.superclass.constructor.call(this, properties);
+
+        this.stage._ADD_TO_FLASH = true;
+        this.stage.fid = createFid(this.stage);
+        this.stage.flashType = "Stage";
+
+        this.commands = properties.commands || [];
+        this.commands.push("create", this.stage.fid, "Stage");
+        this.commands.push("stageAddChild", this.stage.fid);
+    },
+
+    /**
+     * @private
+     * @see Renderer#startDraw
+     */
+    startDraw: function(target){
+        if(target == this.stage){
+            return true;
+        }
+
+        target._lastState = target._lastState || {};
+        //create
+        if(!target._ADD_TO_FLASH){
+            target._ADD_TO_FLASH = true;
+            target.fid = createFid(target);
+
+            if(target._drawTextLine){
+                target.flashType = "Text";
+            }
+            else if(target.beginLinearGradientFill){
+                target.flashType = "Graphics";
+            }
+            else if(target == this.stage){
+                target.flashType = "Stage";
+            }
+            else{
+                target.flashType = "View";
+            }
+            this.commands.push("create", target.fid, target.flashType);
+        }
+
+        return true;
+    },
+
+    /**
+     * @private
+     * @see Renderer#draw
+     */
+    draw: function(target){
+        if(target == this.stage){
+            return;
+        }
+
+        target._lastState = target._lastState || {};
+
+        var lastParent = target._lastState.parent;
+        var parent = target.parent;
+
+        if(parent){
+            if(!lastParent || parent.fid != lastParent.fid){
+                this.commands.push("addChild", parent.fid, target.fid, target.depth);
+            }
+        }
+       
+        target._lastState.parent = target.parent;
+
+        switch(target.flashType){
+            case "Graphics":
+                if(target.isDirty && target.flashGraphicsCommands && target.flashGraphicsCommands.length > 0){
+                    this.commands.push("graphicsDraw", target.fid, target.flashGraphicsCommands.join(";"));
+                    target.isDirty = false;
+                }
+                break;
+            case "Text":
+                break;
+        }
+    },
+
+    /**
+     * @private
+     * @see Renderer#transform
+     */
+    transform: function(target){
+        var stateList = state[target.flashType];
+        var lastState = target._lastState = target._lastState||{};
+        
+        if(stateList){
+            for(var i = 0,l = stateList.length;i < l;i ++){
+                var prop = stateList[i];
+                var lastValue = lastState[prop];
+                var value = target[prop];
+                
+                lastState[prop] = value;
+
+                if(lastValue != value){
+                    this.commands.push("setProp", target.fid, prop, value);
+                }
+            }
+
+            //画图
+            if(target.drawable && target.drawable.image){
+                var image = target.drawable.image;
+                var rect = target.drawable.rect;
+
+                var lastRect = lastState.rect||[];
+                var lastImage = lastState.image||{};
+
+                if(rect && rect.join(",")!= lastRect.join(",")){
+                    this.commands.push("setProp", target.fid, "rect", rect.join(","));
+                }
+
+                if(image && (image.src != lastImage.src)) {
+                    this.commands.push("setImage", target.fid, image.src);
+                }
+
+                lastState.rect = rect;
+                lastState.image = image;
+            }
+        }
+    },
+
+    /**
+     * @private
+     * @see Renderer#remove
+     */
+    remove: function(target){
+        var parent = target.parent;
+        if(parent){
+            this.commands.push("removeChild", target.parent.fid, target.fid);
+            if(target._lastState){
+                target._lastState.parent = null;
+            }
+        }
+    }
+});
+
+})();
+
+/**
+ * @class FlashAdaptor
+ * @module hilo/flash/FlashAdaptor
+ * @requires hilo/core/Hilo
+ * @requires hilo/view/Text
+ * @requires hilo/view/Graphics
+ * @requires hilo/media/WebAudio
+ * @requires hilo/media/WebSound
+ * @requires hilo/view/Stage
+ * @requires hilo/flash/FlashRenderer
+*/
+var FlashAdaptor = (function(){
+
+var scripts = document.scripts;
+var selfScript = scripts[scripts.length - 1];
+var scriptDir = selfScript.src.substring(0, selfScript.src.lastIndexOf('/') + 1);
+var defaultSwf = scriptDir + 'hilo.swf';
+
+var defaultOption = {
+    url: defaultSwf,
+    id: "hiloFlash",
+    width: "100%",
+    height: "100%",
+    color: "#ffffff",
+    fps: 60
+};
+
+var imageCallBacks = {};
+var isFlashReady = false;
+var flashCommands = []; 
+
+var Adaptor = {
+    /**
+     * 初始化flash
+     * @public
+     * @method init
+     * @param {Object} option 参数option定义
+     *      option.url flash网址
+     *      option.fps  flash fps, 默认60
+     *      option.forceFlash 强制falsh模式
+     *      option.id  flash id,默认 hiloFlash    
+     */
+    init:function(option){
+        option = option || {};
+        var that = this;
+
+        if(!Hilo.browser.supportCanvas || option.forceFlash || location.search.indexOf("forceFlash") > -1){
+            Hilo.isFlash = true;
+            this._addFlashCallback();
+            this._flashShim(option);
+        }
+        else{
+            Hilo.View.prototype.release = function(){
+                this.removeFromParent();
+            };
+        }
+    },
+    setFps:function(fps){
+        if(this._fps != fps){
+            this._fps = fps;
+            flashCommands.push("setFps", fps);
+        }
+    },
+    _flashShim:function(option){
+        var that = this;
+        option = Hilo.copy(defaultOption, option||{});
+
+        Array.prototype.indexOf = Array.prototype.indexOf||function(a){
+            for(var i = 0, l = this.length;i > l;i ++){
+                if(this[i] === a){
+                    return i;
+                }
+            }
+            return -1;
+        };
+
+        Hilo.Stage.prototype._initRenderer = function(properties){
+            var canvas = this.canvas;
+            if(typeof canvas === 'string') canvas = Hilo.getElement(canvas);
+
+            var container = properties.container;
+            if(typeof container === 'string') container = Hilo.getElement(container);
+            if(!container) container = document.body;
+            
+            if(canvas && canvas.parentNode){
+                container = container||canvas.parentNode;
+                canvas.parentNode.removeChild(canvas);
+            }
+
+            this.canvas = container;
+            var width = this.width, height = this.height, 
+            viewport = this.updateViewport();
+            if(!properties.width) width = (viewport && viewport.width) || 320;
+            if(!properties.height) height = (viewport && viewport.height) || 480;
+
+            that._insertSwf(Hilo.copy(option, {
+                container:container,
+                width:width * (this.scaleX||1),
+                height:height * (this.scaleY||1)
+            }));
+
+            var props = {canvas:container, stage:this, commands:flashCommands};
+            this.renderer = new FlashRenderer(props);
+        };
+
+        Hilo.Stage.prototype.addTo = function(domElement){
+            var swf = this._swf;
+            if(swf && swf.parentNode !== domElement){
+                domElement.appendChild(swf);
+            }
+            return this;
+        };
+
+        var enableDOMEvent = Hilo.Stage.prototype.enableDOMEvent;
+        Hilo.Stage.prototype.enableDOMEvent = function(type, enabled){
+            var canvas = this.canvas;
+            if(!canvas.addEventListener){
+                canvas.addEventListener = function(type, handler){
+                    canvas.attachEvent('on' + type, handler);
+                };
+                canvas.removeEventListener = function(type, handler){
+                    canvas.detachEvent('on' + type, handler);
+                };
+            }
+
+            return enableDOMEvent.call(this, type, enabled);
+        };
+
+        var onDOMEvent = Hilo.Stage.prototype._onDOMEvent;
+        Hilo.Stage.prototype._onDOMEvent = function(e){
+            onDOMEvent.call(this, e || fixEvent());
+        };
+
+        Hilo.View.prototype.release = function(){
+            this.removeFromParent();
+            if(this.fid){
+                flashCommands.push("release", this.fid);
+            }
+        };
+
+        Hilo.Text.prototype.render = function(renderer){
+            renderer.draw(this);
+        };
+
+        Hilo.Graphics.prototype.render = function(renderer){
+            renderer.draw(this);
+        };
+
+        var graphicsFuncs = [
+            "lineStyle", "beginFill", "endFill",
+            "beginBitmapFill", "beginPath", "closePath", "moveTo", "lineTo", "quadraticCurveTo", "bezierCurveTo",
+            "drawRect", "drawRoundRectComplex", "drawRoundRect", "drawCircle", "drawEllipse", "cache", "uncache", "clear"
+        ];
+
+        //flashGraphicsCommands  command由";"分割 参数由","分割 参数中数组由":"分割
+        for(var i = 0;i < graphicsFuncs.length;i ++){
+            var funcName = graphicsFuncs[i];
+            Hilo.Graphics.prototype[funcName] = function(funcName){
+                return function(){
+                    var args = Array.prototype.slice.call(arguments);
+                    var arr = [funcName].concat(args).join(",");
+
+                    this.flashGraphicsCommands = this.flashGraphicsCommands||[];
+                    this.flashGraphicsCommands.push(arr);
+                    this.isDirty = true;
+                    return this;
+                }
+            }(funcName);
+        }
+
+        Hilo.Graphics.prototype.beginRadialGradientFill = function(x0, y0, r0, x1, y1, r1, colors, ratios){
+            var cmd = ["beginRadialGradientFill", x0, y0, r0, x1, y1, r1, colors.join(":"), ratios.join(":")].join(",");
+            this.flashGraphicsCommands = this.flashGraphicsCommands||[];
+            this.flashGraphicsCommands.push(cmd);
+            this.isDirty = true;
+            return this;
+        };
+
+        Hilo.Graphics.prototype.beginLinearGradientFill = function(x0, y0, x1, y1, colors, ratios){
+            var cmd = ["beginLinearGradientFill", x0, y0, x1, y1, colors.join(":"), ratios.join(":")].join(",");
+            this.flashGraphicsCommands = this.flashGraphicsCommands||[];
+            this.flashGraphicsCommands.push(cmd);
+            this.isDirty = true;
+            return this;
+        };
+
+        Hilo.Graphics.prototype.drawSVGPath = function(pathData){
+            var me = this, addAction = me._addAction,
+                path = pathData.split(/,| (?=[a-zA-Z])/);
+            
+            me.beginPath();
+            for(var i = 0, len = path.length; i < len; i++){
+                var str = path[i], cmd = str.charAt(0).toUpperCase(), p = str.substring(1).split(/,| /);
+                if(p[0].length == 0) p.shift();
+
+                switch(cmd){
+                    case 'M':
+                        me.moveTo(p[0], p[1]);
+                        break;
+                    case 'L':
+                        me.lineTo(p[0], p[1]);
+                        break;
+                    case 'C':
+                        me.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
+                        break;
+                    case 'Z':
+                        me.closePath();
+                        break;
+                }
+            }
+            return me;
+        };
+
+        Hilo.WebSound.removeAudio = function(source){
+            var src = typeof source === 'string' ? source : source.src;
+            var audio = this._audios[src];
+            if(audio){
+                audio.stop();
+                audio.off();
+                audio.release();
+                this._audios[src] = null;
+                delete this._audios[src];
+            }
+        };
+
+        Hilo.WebAudio.isSupported = true;
+        Hilo.WebAudio.enabled = true;
+        Hilo.WebAudio.enable = function(){};
+
+        Hilo.WebAudio.prototype._init = function(){
+            this.fid = Hilo.getUid("audio");
+            flashCommands.push("audio", "create", this.fid, this.src);
+            if(this.autoPlay){
+                this.play();
+            }
+        };
+
+        Hilo.WebAudio.prototype.load = function(){
+            flashCommands.push("audio", "load", this.fid);
+            return this;
+        };
+
+        Hilo.WebAudio.prototype.play = function(){
+            flashCommands.push("audio", "play", this.fid, this.loop?1:0);
+            return this;
+        };
+
+        Hilo.WebAudio.prototype.pause = function(){
+            flashCommands.push("audio", "pause", this.fid);
+            return this;
+        };
+
+        Hilo.WebAudio.prototype.resume = function(){
+            flashCommands.push("audio", "resume", this.fid);
+            return this;
+        };
+
+        Hilo.WebAudio.prototype.stop = function(){
+            flashCommands.push("audio", "stop", this.fid);
+            return this;
+        };
+        
+        Hilo.WebAudio.prototype.setVolume = function(volume){
+            flashCommands.push("audio", "setVolume", this.fid, volume);
+            return this;
+        };
+
+        Hilo.WebAudio.prototype.setMute = function(muted){
+            flashCommands.push("audio", "setMute", this.fid, muted?1:0);
+            return this;
+        };
+
+        Hilo.WebAudio.prototype.release = function(){
+            flashCommands.push("audio", "release", this.fid);
+            return this;
+        };
+    },
+    _insertSwf:function(option){
+        var that = this;
+        var swf;
+        var src = option.url;
+        var id = option.id;
+        var color = option.color||null;
+        var fps = option.fps;
+        var container = option.container;
+        var width = option.width;
+        var height = option.height;
+        
+        this.setFps(fps);
+
+        if(window.attachEvent){
+            var hasHTML = container.innerHTML;
+            container.innerHTML = 
+            '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"' +
+            ' codebase="' + location.protocol + '//fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0">' +
+            '<param name="allowScriptAccess" value="always">' +
+            '<param name="flashvars" value="id=' + id + '">' +
+            '<param name="wmode" value="transparent">' +
+            '<param name="bgcolor" value="' + color + '">' +
+            '</object>' + hasHTML;
+            var swf = container.getElementsByTagName("object")[0];
+            swf["movie"] = src;
+        }
+        else{
+            swf = document.createElement("embed");
+            swf.setAttribute("src",src);
+            swf.setAttribute("type","application/x-shockwave-flash");
+            swf.setAttribute("allowScriptAccess","always");
+            swf.setAttribute("allowFullScreen","true");
+            swf.setAttribute("bgcolor",color);
+            swf.setAttribute("pluginspage","http://www.adobe.com/go/getflashplayer_cn");
+            swf.setAttribute("wmode", "transparent");
+            swf.setAttribute("FlashVars", "debug=0");
+            container.appendChild(swf);
+        }
+        swf.name = id;
+        swf.width = width;
+        swf.height = height;
+        swf.id = id;
+            
+        this._swf = swf;
+        setInterval(function(){
+            that.tick();
+        }, 1000/fps)
+        return swf;
+    },
+    tick:function(){
+        if(isFlashReady && flashCommands.length > 0){
+            this._swf.CallFunction(
+                '<invoke name="executeCommand" returntype="javascript"><arguments><string>'
+                + flashCommands.join("√") + '</string></arguments></invoke>'
+            );
+            // console.log("executeCommand", flashCommands.join(","));
+            flashCommands.length = 0;
+        }
+    },
+    _addFlashCallback:function(){
+        /*
+         * 加载flash图片
+         * @method loadFlashImage
+        */
+        Hilo.loadFlashImage = function(src, successHandler, errorHandler){
+            imageCallBacks[src] = imageCallBacks[src]||[];
+            imageCallBacks[src].push([successHandler, errorHandler||successHandler]);
+            flashCommands.push("loadImage", src);
+        };
+
+        /*
+         *flash 可以调接口时回调函数
+        */
+        Hilo.unlock = function(){
+            isFlashReady = true;
+        };
+
+        /*
+         * falsh图片加载完回调函数
+         * @argument src:图片地址
+         * @argument errorCode: 0:图片加载成功, 1:图片加载失败
+         * @argument width:图片宽
+         * argument height:图片高
+        */
+        Hilo.imageCallBack = function(src, errorCode, width, height){
+            // console.log("imageCallBack", src, errorCode);
+            var arr = imageCallBacks[src];
+            if(arr && arr.length > 0){
+                for(var i = 0, l = arr.length;i < l;i ++){
+                    arr[i][errorCode]({
+                        target:{
+                            src:src, 
+                            width:width, 
+                            height:height,
+                            isFlash:true
+                        },
+                        errorCode:errorCode
+                    });
+                }
+                arr.length = 0;
+            }
+        }
+    }
+};
+
+function fixEvent(){
+    var event = window.event; 
+    var e = {
+        rawEvent:event,
+        type:event.type,
+        target:event.srcElememt,
+        preventDefault:function(){
+            event.returnValue = false;
+        },
+        stopPropagation:function(){
+            event.cancelBubble = true;
+        }
+    };
+    
+    if(event.type.indexOf("mouse") != -1){
+        e.clientX = event.clientX;
+        e.clientY = event.clientY;
+        if(event.type == "mouseover") 
+            e.relatedTarget = event.fromElement; 
+        else if(event.type == "mouseout") 
+            e.relatedTarget = event.toElement; 
+    }
+    else if(event.type.indexOf("key") != -1){
+        e.charCode = e.keyCode = event.keyCode; 
+    }
+    return e; 
+}
+
+if(selfScript.getAttribute('data-auto') === 'true') Adaptor.init();
+return Hilo.FlashAdaptor = Adaptor;
+
+})();
+
+})();

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 8 - 0
flappy/hilo/hilo-standalone.js


binární
flappy/images/bg.png


binární
flappy/images/bird.png


binární
flappy/images/ground.png


binární
flappy/images/holdback.png


binární
flappy/images/number.png


binární
flappy/images/over.png


binární
flappy/images/ready.png


+ 48 - 0
flappy/index.html

@@ -0,0 +1,48 @@
+<!doctype html>
+
+<head>
+    <title>Flappy Bird</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1, maximum-scale=1" />
+    <meta name="apple-mobile-web-app-capable" content="yes" />
+    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
+    <link rel="apple-touch-icon" href="images/icon.png" />
+    <link rel="apple-touch-startup-image" href="images/icon.png" />
+
+    <style type="text/css">
+        body,
+        div,
+        canvas {
+            image-rendering: optimizeSpeed;
+            -webkit-image-rendering: optimizeSpeed;
+            -webkit-interpolation-mode: nearest-neighbor;
+        }
+
+        body {
+            padding: 0;
+            margin: 0;
+            font-size: 12px;
+            background-color: #fff;
+        }
+
+        body,
+        html {
+            height: 100%;
+        }
+    </style>
+
+    <script type="text/javascript" src="hilo/hilo-standalone.js"></script>
+    <script type="text/javascript" src="hilo/hilo-flash.js" data-auto="true"></script>
+    <script type="text/javascript" src="src/game.js"></script>
+    <script type="text/javascript" src="src/Asset.js"></script>
+    <script type="text/javascript" src="src/ReadyScene.js"></script>
+    <script type="text/javascript" src="src/OverScene.js"></script>
+    <script type="text/javascript" src="src/Bird.js"></script>
+    <script type="text/javascript" src="src/Holdbacks.js"></script>
+
+</head>
+
+<body>
+</body>
+
+</html>

+ 71 - 0
flappy/src/Asset.js

@@ -0,0 +1,71 @@
+
+(function (ns) {
+
+    var Asset = ns.Asset = Hilo.Class.create({
+        Mixes: Hilo.EventMixin,
+
+        queue: null,
+        bg: null,
+        ground: null,
+        ready: null,
+        over: null,
+        numberGlyphs: null,
+        birdAtlas: null,
+        holdback: null,
+
+        load: function () {
+            var resources = [
+                { id: 'bg', src: 'images/bg.png' },
+                { id: 'ground', src: 'images/ground.png' },
+                { id: 'ready', src: 'images/ready.png' },
+                { id: 'over', src: 'images/over.png' },
+                { id: 'number', src: 'images/number.png' },
+                { id: 'bird', src: 'images/bird.png' },
+                { id: 'holdback', src: 'images/holdback.png' }
+            ];
+
+            this.queue = new Hilo.LoadQueue();
+            this.queue.add(resources);
+            this.queue.on('complete', this.onComplete.bind(this));
+            this.queue.start();
+        },
+
+        onComplete: function (e) {
+            this.bg = this.queue.get('bg').content;
+            this.ground = this.queue.get('ground').content;
+            this.ready = this.queue.get('ready').content;
+            this.over = this.queue.get('over').content;
+            this.holdback = this.queue.get('holdback').content;
+
+            this.birdAtlas = new Hilo.TextureAtlas({
+                image: this.queue.get('bird').content,
+                frames: [
+                    [0, 120, 86, 60],
+                    [0, 60, 86, 60],
+                    [0, 0, 86, 60]
+                ],
+                sprites: {
+                    bird: [0, 1, 2]
+                }
+            });
+
+            var number = this.queue.get('number').content;
+            this.numberGlyphs = {
+                0: { image: number, rect: [0, 0, 60, 91] },
+                1: { image: number, rect: [61, 0, 60, 91] },
+                2: { image: number, rect: [121, 0, 60, 91] },
+                3: { image: number, rect: [191, 0, 60, 91] },
+                4: { image: number, rect: [261, 0, 60, 91] },
+                5: { image: number, rect: [331, 0, 60, 91] },
+                6: { image: number, rect: [401, 0, 60, 91] },
+                7: { image: number, rect: [471, 0, 60, 91] },
+                8: { image: number, rect: [541, 0, 60, 91] },
+                9: { image: number, rect: [611, 0, 60, 91] }
+            };
+
+            this.queue.off('complete');
+            this.fire('complete');
+        }
+    });
+
+})(window.game);

+ 80 - 0
flappy/src/Bird.js

@@ -0,0 +1,80 @@
+
+(function (ns) {
+
+    var Bird = ns.Bird = Hilo.Class.create({
+        Extends: Hilo.Sprite,
+        constructor: function (properties) {
+            Bird.superclass.constructor.call(this, properties);
+
+            this.addFrame(properties.atlas.getSprite('bird'));
+            this.interval = 6;
+            this.pivotX = 43;
+            this.pivotY = 30;
+
+            this.gravity = 10 / 1000 * 0.3;
+            this.flyHeight = 80;
+            this.initVelocity = Math.sqrt(2 * this.flyHeight * this.gravity);
+        },
+
+        startX: 0, //小鸟的起始x坐标
+        startY: 0, //小鸟的起始y坐标
+        groundY: 0, //地面的坐标
+        gravity: 0, //重力加速度
+        flyHeight: 0, //小鸟每次往上飞的高度
+        initVelocity: 0, //小鸟往上飞的初速度
+
+        isDead: true, //小鸟是否已死亡
+        isUp: false, //小鸟是在往上飞阶段,还是下落阶段
+        flyStartY: 0, //小鸟往上飞的起始y轴坐标
+        flyStartTime: 0, //小鸟飞行起始时间
+
+        getReady: function () {
+            //设置起始坐标
+            this.x = this.startX;
+            this.y = this.startY;
+
+            this.rotation = 0;
+            this.interval = 6;
+            this.play();
+            this.tween = Hilo.Tween.to(this, { y: this.y + 10, rotation: -8 }, { duration: 400, reverse: true, loop: true });
+        },
+
+        startFly: function () {
+            this.isDead = false;
+            this.interval = 3;
+            this.flyStartY = this.y;
+            this.flyStartTime = +new Date();
+            if (this.tween) this.tween.stop();
+        },
+
+        onUpdate: function () {
+            if (this.isDead) return;
+
+            //飞行时间
+            var time = (+new Date()) - this.flyStartTime;
+            //飞行距离
+            var distance = this.initVelocity * time - 0.5 * this.gravity * time * time;
+            //y轴坐标
+            var y = this.flyStartY - distance;
+
+            if (y <= this.groundY) {
+                //小鸟未落地
+                this.y = y;
+                if (distance > 0 && !this.isUp) {
+                    //往上飞时,角度上仰20度
+                    this.tween = Hilo.Tween.to(this, { rotation: -20 }, { duration: 200 });
+                    this.isUp = true;
+                } else if (distance < 0 && this.isUp) {
+                    //往下跌落时,角度往下90度
+                    this.tween = Hilo.Tween.to(this, { rotation: 90 }, { duration: this.groundY - this.y });
+                    this.isUp = false;
+                }
+            } else {
+                //小鸟已经落地,即死亡
+                this.y = this.groundY;
+                this.isDead = true;
+            }
+        }
+    });
+
+})(window.game);

+ 166 - 0
flappy/src/Holdbacks.js

@@ -0,0 +1,166 @@
+
+(function(ns){
+
+var Holdbacks = ns.Holdbacks = Hilo.Class.create({
+    Extends: Hilo.Container,
+    constructor: function(properties){
+        Holdbacks.superclass.constructor.call(this, properties);
+        
+        //管子之间的水平间隔
+        this.hoseSpacingX = 300;
+        //上下管子之间的垂直间隔,即小鸟要穿越的空间大小
+        this.hoseSpacingY = 290;
+        //管子的总数(上下一对管子算一个)
+        this.numHoses = 4;
+        //移出屏幕左侧的管子数量,一般设置为管子总数的一半
+        this.numOffscreenHoses = this.numHoses * 0.5;
+        //管子的宽度(包括管子之间的间隔)
+        this.hoseWidth = 148 + this.hoseSpacingX;
+
+        //初始化障碍的宽和高
+        this.width = this.hoseWidth * this.numHoses;
+        this.height = properties.height;
+
+        this.reset();
+        this.createHoses(properties.image);
+        this.moveTween = new Hilo.Tween(this, null, {
+            onComplete: this.resetHoses.bind(this)
+        });
+    },
+
+    startX: 0, //障碍开始的起始x轴坐标
+    groundY: 0, //地面的y轴坐标
+
+    hoseSpacingX: 0, //管子之间的水平间隔
+    hoseSpacingY: 0, //上下管子之间的垂直间隔
+    numHoses: 0, //管子的总数(上下一对管子算一个)
+    numOffscreenHoses: 0, //移出屏幕左侧的管子数量
+    hoseWidth: 0, //管子的宽度(包括管子之间的间隔)
+
+    passThrough: 0, //穿过的管子的数量,也即移出屏幕左侧的管子的数量
+
+    createHoses: function(image){
+        for(var i = 0; i < this.numHoses; i++){
+            var downHose = new Hilo.Bitmap({
+                id: 'down' + i,
+                image: image,
+                rect: [0, 0, 148, 820],
+                boundsArea:[
+                    {x:8, y:0}, 
+                    {x:140, y:0}, 
+                    {x:140, y:60}, 
+                    {x:136, y:60}, 
+                    {x:136, y:820}, 
+                    {x:14, y:820}, 
+                    {x:14, y:60}, 
+                    {x:8, y:60}
+                ]
+            }).addTo(this);
+
+            var upHose = new Hilo.Bitmap({
+                id: 'up' + i,
+                image: image,
+                rect: [148, 0, 148, 820],
+                boundsArea:[
+                    {x:14, y:0}, 
+                    {x:140, y:0}, 
+                    {x:140, y:820-60}, 
+                    {x:144, y:820-60},
+                    {x:144, y:820}, 
+                    {x:8, y:820}, 
+                    {x:8, y:820-60}, 
+                    {x:14, y:820-60}
+                ]
+            }).addTo(this);
+
+            this.placeHose(downHose, upHose, i);
+        }           
+    },
+
+    placeHose: function(down, up, index){
+        //下面障碍在y轴的最上的位置
+        var downMinY = this.groundY - down.height + this.hoseSpacingY;
+        //下面障碍在y轴的最下的位置
+        var downMaxY = this.groundY - 180;
+        //在downMinY和downMaxY之间随机位置
+        down.y = downMinY + (downMaxY - downMinY) * Math.random() >> 0;
+        down.x = this.hoseWidth * index;
+
+        up.y = down.y - this.hoseSpacingY - up.height;
+        up.x = down.x;
+    },
+
+    resetHoses: function(){
+        var total = this.children.length;
+
+        //把已移出屏幕外的管子放到队列最后面,并重置它们的可穿越位置
+        for(var i = 0; i < this.numOffscreenHoses; i++){
+            var downHose = this.getChildAt(0);
+            var upHose = this.getChildAt(1);
+            this.setChildIndex(downHose, total - 1);
+            this.setChildIndex(upHose, total - 1);
+            this.placeHose(downHose, upHose, this.numOffscreenHoses + i);
+        }
+        
+        //重新确定队列中所有管子的x轴坐标
+        for(var i = 0; i < total - this.numOffscreenHoses * 2; i++){
+            var hose = this.getChildAt(i);
+            hose.x = this.hoseWidth * (i * 0.5 >> 0);
+        }
+
+        //重新确定障碍的x轴坐标
+        this.x = 0;
+
+        //更新穿过的管子数量
+        this.passThrough += this.numOffscreenHoses;
+
+        //继续移动
+        this.startMove();
+        Hilo.Tween._tweens.push(this.moveTween);
+    },
+
+    startMove: function(){
+        //设置缓动的x轴坐标
+        var targetX = -this.hoseWidth * this.numOffscreenHoses;
+        //设置缓动时间
+        this.moveTween.duration = (this.x - targetX) * 4;
+        //设置缓动的变换属性,即x从当前坐标变换到targetX
+        this.moveTween.setProps({x:this.x}, {x:targetX});
+        //启动缓动动画
+        this.moveTween.start();
+    },
+
+    stopMove: function(){
+        if(this.moveTween) this.moveTween.pause();
+    },
+
+    checkCollision: function(bird){
+        for(var i = 0, len = this.children.length; i < len; i++){
+            if(bird.hitTestObject(this.children[i], true)){
+                return true;
+            }
+        }
+        return false;
+    },
+
+    calcPassThrough: function(x){
+        var count = 0;
+
+        x = -this.x + x;
+        if(x > 0){
+            var num = x / this.hoseWidth + 0.5 >> 0;
+            count += num;
+        }
+        count += this.passThrough;
+
+        return count;
+    },
+
+    reset: function(){
+        this.x = this.startX;
+        this.passThrough = 0;
+    }
+    
+});
+
+})(window.game);

+ 105 - 0
flappy/src/OverScene.js

@@ -0,0 +1,105 @@
+
+(function (ns) {
+
+    var OverScene = ns.OverScene = Hilo.Class.create({
+        Extends: Hilo.Container,
+        constructor: function (properties) {
+            OverScene.superclass.constructor.call(this, properties);
+            this.init(properties);
+        },
+
+        init: function (properties) {
+            var board = new Hilo.Bitmap({
+                id: 'board',
+                image: properties.image,
+                rect: [0, 0, 590, 298]
+            });
+
+            var gameover = new Hilo.Bitmap({
+                id: 'gameover',
+                image: properties.image,
+                rect: [0, 298, 508, 158]
+            });
+
+            var startBtn = new Hilo.Bitmap({
+                id: 'start',
+                image: properties.image,
+                rect: [590, 0, 290, 176]
+            });
+
+            var gradeBtn = new Hilo.Bitmap({
+                id: 'grade',
+                image: properties.image,
+                rect: [590, 176, 290, 176]
+            });
+
+            var scoreLabel = new Hilo.BitmapText({
+                id: 'score',
+                glyphs: properties.numberGlyphs,
+                scaleX: 0.5,
+                scaleY: 0.5,
+                letterSpacing: 4
+            });
+
+            var bestLabel = new Hilo.BitmapText({
+                id: 'best',
+                glyphs: properties.numberGlyphs,
+                scaleX: 0.5,
+                scaleY: 0.5,
+                letterSpacing: 4
+            });
+
+            var whiteMask = new Hilo.View({
+                id: 'mask',
+                width: this.width,
+                height: this.height,
+                alpha: 0,
+                background: '#fff'
+            });
+
+            board.x = this.width - board.width >> 1;
+            board.y = this.height - board.height >> 1;
+            gameover.x = this.width - gameover.width >> 1;
+            gameover.y = board.y - gameover.height - 20;
+            startBtn.x = board.x - 5;
+            startBtn.y = board.y + board.height + 20 >> 0;
+            gradeBtn.x = startBtn.x + startBtn.width + 20 >> 0;
+            gradeBtn.y = startBtn.y;
+            scoreLabel.x = board.x + board.width - 140 >> 0;
+            scoreLabel.y = board.y + 90;
+            bestLabel.x = scoreLabel.x;
+            bestLabel.y = scoreLabel.y + 105;
+
+            this.addChild(gameover, board, startBtn, gradeBtn, scoreLabel, bestLabel, whiteMask);
+        },
+
+        show: function (score, bestScore) {
+            this.visible = true;
+            this.getChildById('score').setText(score);
+            this.getChildById('best').setText(bestScore);
+            this.getChildById('mask').alpha = 1;
+
+            Hilo.Tween.to(this.getChildById('gameover'), { alpha: 1 }, { duration: 100 });
+            Hilo.Tween.to(this.getChildById('board'), { alpha: 1, y: this.getChildById('board').y - 150 }, { duration: 200, delay: 200 });
+            Hilo.Tween.to(this.getChildById('score'), { alpha: 1, y: this.getChildById('score').y - 150 }, { duration: 200, delay: 200 });
+            Hilo.Tween.to(this.getChildById('best'), { alpha: 1, y: this.getChildById('best').y - 150 }, { duration: 200, delay: 200 });
+            Hilo.Tween.to(this.getChildById('start'), { alpha: 1 }, { duration: 100, delay: 600 });
+            Hilo.Tween.to(this.getChildById('grade'), { alpha: 1 }, { duration: 100, delay: 600 });
+            Hilo.Tween.to(this.getChildById('mask'), { alpha: 0 }, { duration: 400 });
+        },
+
+        hide: function () {
+            this.visible = false;
+            this.getChildById('gameover').alpha = 0;
+            this.getChildById('board').alpha = 0;
+            this.getChildById('score').alpha = 0;
+            this.getChildById('best').alpha = 0;
+            this.getChildById('start').alpha = 0;
+            this.getChildById('grade').alpha = 0;
+            this.getChildById('board').y += 150;
+            this.getChildById('score').y += 150;
+            this.getChildById('best').y += 150;
+        }
+    });
+
+})(window.game);

+ 34 - 0
flappy/src/ReadyScene.js

@@ -0,0 +1,34 @@
+
+(function(ns){
+
+var ReadyScene = ns.ReadyScene = Hilo.Class.create({
+    Extends: Hilo.Container,
+    constructor: function(properties){
+        ReadyScene.superclass.constructor.call(this, properties);
+        this.init(properties);
+    },
+
+    init: function(properties){
+        //准备Get Ready!
+        var getready = new Hilo.Bitmap({
+            image: properties.image,
+            rect: [0, 0, 508, 158]
+        });
+
+        //开始提示tap
+        var tap = new Hilo.Bitmap({
+            image: properties.image,
+            rect: [0, 158, 286, 246]
+        });
+        
+        //确定getready和tap的位置
+        tap.x = this.width - tap.width >> 1;
+        tap.y = this.height - tap.height + 40 >> 1;
+        getready.x = this.width - getready.width >> 1;
+        getready.y = tap.y - getready.height >> 0;
+
+        this.addChild(tap, getready);
+    }
+});
+
+})(window.game);

+ 249 - 0
flappy/src/game.js

@@ -0,0 +1,249 @@
+(function(){
+
+window.onload = function(){
+    game.init();
+}
+
+var game = window.game = {
+    width: 0,
+    height: 0,
+
+    asset: null,
+    stage: null,
+    ticker: null,
+    state: null,
+    score: 0,
+
+    bg: null,
+    ground: null,
+    bird: null,
+    holdbacks: null,
+    gameReadyScene: null,
+    gameOverScene: null,
+
+    init: function(){
+        this.asset = new game.Asset();
+        this.asset.on('complete', function(e){
+            this.asset.off('complete');
+            this.initStage();
+        }.bind(this));
+        this.asset.load();
+    },
+
+    initStage: function(){
+        this.width = Math.min(innerWidth, 450) * 2;
+        this.height = Math.min(innerHeight, 750) * 2;
+        this.scale = 0.5;
+
+        //舞台画布
+        var canvasType = location.search.indexOf('dom') != -1 ? 'div' : 'canvas';
+        var canvas = Hilo.createElement(canvasType);
+
+        //舞台
+        this.stage = new Hilo.Stage({
+            canvas: canvas,
+            width: this.width,
+            height: this.height,
+            scaleX: this.scale,
+            scaleY: this.scale
+        }).addTo(document.body);
+
+        //启动计时器
+        this.ticker = new Hilo.Ticker(60);
+        this.ticker.addTick(Hilo.Tween);
+        this.ticker.addTick(this.stage);
+        this.ticker.start(true);
+
+        //绑定交互事件
+        this.stage.enableDOMEvent(Hilo.event.POINTER_START, true);
+        this.stage.on(Hilo.event.POINTER_START, this.onUserInput.bind(this));
+
+        //Space键控制
+        if(document.addEventListener){
+            document.addEventListener('keydown', function(e){
+                if(e.keyCode === 32) this.onUserInput(e);
+            }.bind(this));
+        }else{
+            document.attachEvent('onkeydown', function(e){
+                if(e.keyCode === 32) this.onUserInput(e);
+            }.bind(this));
+        }
+        
+        //舞台更新
+        this.stage.onUpdate = this.onUpdate.bind(this);
+
+        //初始化
+        this.initBackground();
+        this.initScenes();
+        this.initHoldbacks();
+        this.initBird();
+        this.initCurrentScore();
+
+        //准备游戏
+        this.gameReady();
+    },
+
+    initBackground: function(){
+        //背景
+        var bgWidth = this.width * this.scale;
+        var bgHeight = this.height * this.scale;
+
+        var bgImg = this.asset.bg;
+        this.bg = new Hilo.Bitmap({
+            id: 'bg',
+            image: bgImg,
+            scaleX:this.width / bgImg.width,
+            scaleY:this.height / bgImg.height
+        }).addTo(this.stage);
+
+        //地面
+        var groundImg = this.asset.ground;
+        var groundOffset = 60;
+        this.ground = new Hilo.Bitmap({
+            id: 'ground',
+            image: groundImg,
+            scaleX:(this.width + groundOffset * 2) / groundImg.width
+        }).addTo(this.stage);
+
+        //设置地面的y轴坐标
+        this.ground.y = this.height - this.ground.height;
+        
+        //移动地面
+        Hilo.Tween.to(this.ground, {x:-groundOffset * this.ground.scaleX}, {duration:400, loop:true});
+    },
+
+    initCurrentScore: function(){
+        //当前分数
+        this.currentScore = new Hilo.BitmapText({
+            id: 'score',
+            glyphs: this.asset.numberGlyphs,
+            textAlign:'center'
+        }).addTo(this.stage);
+
+        //设置当前分数的位置
+        this.currentScore.x = this.width - this.currentScore.width >> 1;
+        this.currentScore.y = 180;
+    },
+
+    initBird: function(){
+        this.bird = new game.Bird({
+            id: 'bird',
+            atlas: this.asset.birdAtlas,
+            startX: 100,
+            startY: this.height >> 1,
+            groundY: this.ground.y - 12
+        }).addTo(this.stage, this.ground.depth - 1);
+    },
+
+    initHoldbacks: function(){
+        this.holdbacks = new game.Holdbacks({
+            id: 'holdbacks',
+            image: this.asset.holdback,
+            height: this.height,
+            startX: this.width + 200,
+            groundY: this.ground.y
+        }).addTo(this.stage, this.ground.depth - 1);
+    },
+
+    initScenes: function(){
+        //准备场景
+        this.gameReadyScene = new game.ReadyScene({
+            id: 'readyScene',
+            width: this.width,
+            height: this.height,
+            image: this.asset.ready
+        }).addTo(this.stage);
+
+        //结束场景
+        this.gameOverScene = new game.OverScene({
+            id: 'overScene',
+            width: this.width,
+            height: this.height,
+            image: this.asset.over,
+            numberGlyphs: this.asset.numberGlyphs,
+            visible: false
+        }).addTo(this.stage);
+
+        //绑定开始按钮事件
+        this.gameOverScene.getChildById('start').on(Hilo.event.POINTER_START, function(e){
+            e.stopImmediatePropagation && e.stopImmediatePropagation();
+            this.gameReady();
+        }.bind(this));
+    },
+
+    onUserInput: function(e){
+        if(this.state !== 'over' && !this.gameOverScene.contains(e.eventTarget)){
+            //启动游戏场景
+            if(this.state !== 'playing') this.gameStart();
+            //控制小鸟往上飞
+            this.bird.startFly();
+        }
+    },
+
+    onUpdate: function(delta){
+        if(this.state === 'ready'){
+            return;
+        }
+
+        if(this.bird.isDead){
+            this.gameOver();
+        }else{
+            this.currentScore.setText(this.calcScore());
+            //碰撞检测
+            if(this.holdbacks.checkCollision(this.bird)){
+                this.gameOver();
+            }
+        }
+    },
+
+    gameReady: function(){
+        this.gameOverScene.hide();
+        this.state = 'ready';
+        this.score = 0;
+        this.currentScore.visible = true;
+        this.currentScore.setText(this.score);
+        this.gameReadyScene.visible = true;
+        this.holdbacks.reset();
+        this.bird.getReady();
+    },
+
+    gameStart: function(){
+        this.state = 'playing';
+        this.gameReadyScene.visible = false;
+        this.holdbacks.startMove();
+    },
+
+    gameOver: function(){
+        if(this.state !== 'over'){
+            //设置当前状态为结束over
+            this.state = 'over';
+            //停止障碍的移动
+            this.holdbacks.stopMove();
+            //小鸟跳转到第一帧并暂停
+            this.bird.goto(0, true);
+            //隐藏屏幕中间显示的分数
+            this.currentScore.visible = false;
+            //显示结束场景
+            this.gameOverScene.show(this.calcScore(), this.saveBestScore());
+        }
+    },
+
+    calcScore: function(){
+        var count = this.holdbacks.calcPassThrough(this.bird.x);
+        return this.score = count;
+    },
+
+    saveBestScore: function(){
+        var score = this.score, best = 0;
+        if(Hilo.browser.supportStorage){
+            best = parseInt(localStorage.getItem('hilo-flappy-best-score')) || 0;
+        }
+        if(score > best){
+            best = score;
+            localStorage.setItem('hilo-flappy-best-score', score);
+        }
+        return best;
+    }
+};
+
+})();

+ 3 - 0
game_test1/src/libs/hilo/Hilo.ts

@@ -0,0 +1,3 @@
+import HiloMin from "./HiloMin.js";
+
+export default { ...window.Hilo };

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 24 - 0
game_test1/src/libs/hilo/HiloMin.js


+ 14 - 35
game_test1/src/pages/hilo/index.vue

@@ -1,9 +1,9 @@
 <template>
 	<view class="page">
-		<view class="canvas_box">
-			<view class="tc p20">canvas演示</view>
+		<view class="canvas_box pos_r">
+			<view class="tc p20 titlebar">canvas演示</view>
 			<!-- <canvas id="canvasId" class="canvas" /> -->
-			<div id="game-container"></div>
+			<div id="game-container" class="bg_ff"></div>
 		</view>
 	</view>
 </template>
@@ -15,32 +15,14 @@ import Page from "../../libs/pages/Page";
 
 const title = ref("Hello");
 let stage;
-let gameContainer = document.getElementById("game-container");
-const init = () => {
-	console.log("#debug#🚀 ~ file: index.vue:17 ~ init ~ init:开始啊", Hilo);
-	stage = new Hilo.Stage({
-		renderType: "canvas",
-		container: gameContainer,
-		width: 320,
-		height: 480
-	});
-	console.log("#debug#🚀 ~ file: index.vue:24 ~ init ~ stage:", stage);
 
-	var ticker = new Hilo.Ticker(60);
-	ticker.addTick(stage);
-	ticker.start();
+const init = () => {
+	let gameContainer: any = document.getElementById("game-container");
+	let sysinfo = uni.getSystemInfoSync();
+	console.log("#debug#🚀 ~ file: index.vue:22 ~ init ~ sysinfo:", sysinfo);
 
-	var font = "14px arial";
-	var content = "Hello World! Hilo是一款HTML5 2D游戏引擎,欢迎使用。";
-	var text = new Hilo.Text({
-		font: font,
-		text: content,
-		lineSpacing: 0,
-		width: 250,
-		height: 100,
-		x: 40,
-		y: 50
-	}).addTo(stage);
+	gameContainer.style.height = sysinfo.safeArea?.height + "px";
+	gameContainer.style.width = sysinfo.safeArea?.right + "px";
 };
 
 onLoad(async () => {
@@ -51,14 +33,11 @@ onLoad(async () => {
 
 <style lang="scss" scoped>
 .page {
-	.canvas_box {
-		background-color: #f5f5ff;
-		height: 100vh;
-		.canvas {
-			width: 100%;
-			height: 500rpx;
-			background-color: #fff;
-		}
+	.titlebar {
+		position: absolute;
+		width: 100%;
+		left: 0;
+		top: 0;
 	}
 }
 </style>

binární
game_test1/src/static/images/bom.png


binární
game_test1/src/static/images/man.png


binární
game_test1/src/static/images/map.jpg


+ 19 - 5
game_test1/tsconfig.json

@@ -1,13 +1,27 @@
 {
   "extends": "@vue/tsconfig/tsconfig.json",
   "compilerOptions": {
+    "ignoreDeprecations": "5.0",
     "sourceMap": true,
     "baseUrl": ".",
     "paths": {
-      "@/*": ["./src/*"]
+      "@/*": [
+        "src/*"
+      ]
     },
-    "lib": ["esnext", "dom"],
-    "types": ["@dcloudio/types"]
+    "lib": [
+      "esnext",
+      "dom"
+    ],
+    "types": [
+      "@dcloudio/types"
+    ]
   },
-  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
-}
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.d.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue",
+    "src/libs/hilo/Hilo.js"
+  ]
+}

+ 30 - 1
hilo_html/index.html

@@ -20,12 +20,41 @@
     <script type="text/javascript" src="./static/js/demo.js"></script>
     <script type="text/javascript">
 
+        //舞台画布
+        var canvasType = location.search.indexOf("dom") != -1 ? "div" : "canvas";
+        var canvas = Hilo.createElement(canvasType);
+
+        console.log("#debug#🚀 ~ file: index.vue:17 ~ init ~ init:开始啊", Hilo);
         var stage = new Hilo.Stage({
             renderType: renderType,
             container: gameContainer,
             width: stageWidth,
             height: stageHeight
         });
+        console.log("#debug#🚀 ~ file: index.vue:24 ~ init ~ stage:", stage);
+
+        var ticker = new Hilo.Ticker(60);
+        ticker.addTick(stage);
+        ticker.start();
+
+        var font = "14px arial";
+        var content = "Hello World! Hilo是一款HTML5 2D游戏引擎,欢迎使用。";
+        var text = new Hilo.Text({
+            font: font,
+            text: content,
+            lineSpacing: 0,
+            width: 250,
+            height: 100,
+            x: 40,
+            y: 50
+        }).addTo(stage);
+
+        /* var stage = new Hilo.Stage({
+            renderType: renderType,
+            container: gameContainer,
+            width: stageWidth,
+            height: stageHeight
+        });
 
         //start stage ticker
         var ticker = new Hilo.Ticker(60);
@@ -154,7 +183,7 @@
                 x, y: 100
             }
 
-        }
+        } */