博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第三章 坦克大战
阅读量:4135 次
发布时间:2019-05-25

本文共 23409 字,大约阅读时间需要 78 分钟。


今天写一写模拟类的算法,如果只是模拟一些答案,比如两个人对战掉多少血,最后谁赢谁输,没有什么乐趣,今天就用javascript来实现一个坦克大战的游戏,最后的界面如下:

上面的地图是我任意画上去的,代码中有详细解释,游戏过程如下:
1、红色的坦克是己方坦克,可以通过上下左右四个方向键控制方向,空格键发射子弹
2、页面中最多同时存在3辆敌方坦克,歼灭一辆敌方坦克时,2秒后在一辆新的坦克会出现
3、共计有20辆敌方坦克
4、有四种障碍物,树(图中没有画出)、石头、砖块、水流,其中
    树和石头效果相同,子弹和坦克都不能通过;
    子弹可以打破砖块;
    水可以通过子弹,但不能通过坦克。
呵呵,看到上图我步的障碍物了吗?貌似老家敌方坦克是打不到的,我无敌了
首先,先把公共函数和配置文件贴上:
Common.js
//一些公共函数//获取IDvar $ = function (id) { return typeof id === "string" ? document.getElementById(id) : id };//获取tagNamevar $$ = function (tagName, oParent) { return (oParent || document).getElementsByTagName(tagName) };//获取classvar $$$ = function (sClass, oParent) {    var aClass = [],    i = 0,    reClass = new RegExp("(\\s|^)" + sClass + "($|\\s)"),    aElement = $$("*", oParent);    for (i = 0; i < aElement.length; i++) reClass.test(aElement[i].className) && aClass.push(aElement[i]);    return aClass};Config.jsvar Resource = {    ROOT: "images/",    IMG_HOME: "home.bmp",    IMG_TREE: "tree.bmp",    IMG_WATER: "water.bmp",    IMG_IRON: "iron.bmp",    IMG_BLOCK: "block.bmp",    IMG_SBLOCK: "smallblock.bmp",    IMG_TANKS_NORTH: "tanks_north.bmp",    IMG_TANKS_SOUTH: "tanks_south.bmp",    IMG_TANKS_WEST: "tanks_west.bmp",    IMG_TANKS_EAST: "tanks_east.bmp",    IMG_TANKE_NORTH: "tanke_north.bmp",    IMG_TANKE_SOUTH: "tanke_south.bmp",    IMG_TANKE_WEST: "tanke_west.bmp",    IMG_TANKE_EAST: "tanke_east.bmp",    IMG_BULLET: "hitterg.bmp"};var GameConfig = {    MapWidth: 800,//地图宽度    MapHeight: 500,//地图高度    TanksSpeed: 3,//我方坦克移动速度    TankeSpeed: 3,//敌方坦克移动速度    BulletSpeed: 9//子弹速度}//单位类型var Type = {    TankHuman: 0x1, //我方坦克    TankComputer: 0x2, //敌方坦克    BulletHuman: 0x4, //我方坦克炮弹    BulletComputer: 0x8, //敌方坦克炮弹    Home: 0x10,//老家    Block: 0x20,//钻块    All: 0xFFFF}
然后,正式开始我们的思考,
1、在一张图中,我们有己方坦克、敌方坦克、老家、树、水流等元素其中树、水流、石头这些是静态元素(不包括砖头),只是占据一定的空间,但是没有任何作用
2、砖头的效果和老家差不多,碰到子弹就会消掉,所以砖头和老家应该可以看为一类
3、己方坦克和敌方坦克的区别在于,敌方坦克随机移动位置和发射子弹,己方通过键盘操作,因为碰到子弹后会消失,所以己方坦克和敌方坦克可以和砖头、老家共用一个类,都共同实现了碰到子弹后的操作
4、己方和敌方发射的子弹只对对方有效,所以要分两种子弹考虑
所以我们共分两大类,一个是地图类(树、石头、水)、一个是游戏单元类(包含剩下的元素),但是他们都有很多共同点,比如都是在一张图中,都需要判断元素是否包含,都有位置信息等,所以我们先实现他们共同的基类:BaseUnit类
BaseUnit.js
//向量操作var Vector = new Array();Vector.Cross = function (v1, v2) {    return (v1.x * v2.y - v1.y * v2.x);}Vector.Length = function (v1) {    return (Math.sqrt(v1.x * v2.x + v1.y * v2.y));}function BaseUnit() { } //游戏单位BaseUnit.prototype = {    //判断两个物件或者区域是否相交,若相交,则要么顶点互相包含,要么矩形边界(或对角线)相交    Intersects : function (obj) {        if (this.pointInside(obj.posx, obj.posy) ||   this.pointInside(obj.posx + obj.width * obj.unitWidth, obj.posy) ||   this.pointInside(obj.posx, obj.posy + obj.height * obj.unitHeight) ||   this.pointInside(obj.posx + obj.width * obj.unitWidth, obj.posy + obj.height * obj.unitHeight)) {            return true;        }        else if (obj.pointInside(this.posx, this.posy) ||   obj.pointInside(this.posx + this.width * this.unitWidth, this.posy) ||   obj.pointInside(this.posx, this.posy + this.height * this.unitHeight) ||   obj.pointInside(this.posx + this.width * this.unitWidth, this.posy + this.height * this.unitHeight)) {            return true;        }        else if (obj.posx != null && obj.posy != null)//判断矩形对角线相交 |v1 X v2||v1 X v3| < 0        {            var vector1 = new Object();            var vector2 = new Object();            var vector3 = new Object();            vector1.x = this.width * this.unitWidth;            vector1.y = this.height * this.unitHeight; //矩形对角线向量            vector2.x = obj.posx - this.posx;            vector2.y == obj.posy - this.posy;            vector3.x = vector2.x + obj.unitWidth * obj.width;            vector3.y = vector2.y + obj.unitHeight * obj.Height;            if ((Vector.Cross(vector1, vector2) * Vector.Cross(vector1, vector3)) < 0) {                return true;            }        }        return false;    },    pointInside : function (x, y) //判断一个点是否在这个物件内部    {        if (this.posx == null || this.posy == null) {            return false;        }        if (x >= this.posx && x <= this.posx + this.width * this.unitWidth && y >= this.posy && y <= this.posy + this.height * this.unitHeight) {            return true;        }        return false;    },    getPosition : function () //返回物件的重心,因为是矩形所以返回中点    {        var pos = new Array();        pos.push(this.posx + this.unitWidth * this.width / 2);        pos.push(this.posy + this.unitHeight * this.height / 2);        return pos;    },    isVisible : function () {        var obj = document.getElementById(this.id);        return obj != null && obj.style.display != 'none';    },    Test : function (posx, posy) {        this.oldpos = new Array(this.posx, this.posy);        this.posx = posx;        this.posy = posy;    },    Recover : function () {        if (this.oldpos != null) {            this.posx = this.oldpos[0];            this.posy = this.oldpos[1];            return true;        }        return false;    }}
接下来我们先实现Region地图类,相对另一个类GameUnit类要容易许多
Regiong.js
//地图区域类function Region(id, width, height, type) {    Region.metaData[id] = this;    Region.metaData.All.push(this);    this.type = type;    this.width = width;    this.height = height;    this.id = id;    Region.prototype._parseType = function () {        this.imageUrl = Region.mapImages[this.type];        switch (type) {            case "tree":                this.allowPassType = 0;	 //规定允许何种类型的Unit通过,0表示不允许任何单位通过                this.absorbType = Type.BulletHuman | Type.BulletComputer; //吸收(使消失)何种类型的Unit                this.unitWidth = 16;                this.unitHeight = 16;                break;            case "water":                this.allowPassType = Type.BulletHuman | Type.BulletComputer;	 //规定允许何种类型的Unit通过,0表示不允许任何单位通过                this.absorbType = 0;                this.unitWidth = 16;                this.unitHeight = 16;                break;            case "iron":                this.allowPassType = 0;	 //规定允许何种类型的Unit通过,0表示不允许任何单位通过                this.absorbType = Type.BulletHuman | Type.BulletComputer; //吸收(使消失)何种类型的Unit;                this.unitWidth = 16;                this.unitHeight = 16;                break;            default:                this.allowPass = 0xFF;                break;        }    }    Region.prototype.Draw = function (posx, posy) {        this.posx = posx;        this.posy = posy;        var backCanvs = $("_back");        var newRegion = $(this.id);        if (newRegion == null) {            newRegion = document.createElement("span");            newRegion.id = this.id;            backCanvs.appendChild(newRegion);        }        newRegion.style.position = "absolute";        newRegion.style.left = this.posx;        newRegion.style.top = this.posy;        newRegion.style.width = this.width * this.unitWidth;        newRegion.style.height = this.height * this.unitHeight;        newRegion.style.backgroundImage = "url(" + this.imageUrl + ")";    }    Region.prototype.Overlaped = function () //判断是否和其他区域重叠    {        if (this.posx == null || this.posy == null) {            return false;        }        for (var i = 0; i < Region.metaData.All.length; i++) {            if (this == Region.metaData.All[i] || Region.metaData.All[i].posx == null || Region.metaData.All[i].posy == null) {                continue;            }            if (this.Intersects(Region.metaData.All[i])) {                return true;            }        }        return false;    }    Region.prototype.Dispose = function () {        var backCanvs = $("_back");        var unit = $(this.id);        backCanvs.removeChild(unit);        Region.metaData[this.id] = null;        for (var i = 0; i < Region.metaData.All.length; i++) {            if (Region.metaData.All[i].id == this.id) {                Region.metaData.All.splice(i, 1);            }        }        this.Disposed = true;    }    this._parseType();} Region.prototype = new BaseUnit();Region.mapImages = new Array();//保存图片路劲Region.mapImages["tree"] = Resource.ROOT + Resource.IMG_TREE;Region.mapImages["water"] = Resource.ROOT + Resource.IMG_WATER;Region.mapImages["iron"] = Resource.ROOT + Resource.IMG_IRON;Region.mapImages["block"] = Resource.ROOT + Resource.IMG_BLOCK;Region.metaData = new Array();Region.metaData["RandomRegions"] = new Array();Region.metaData.All = new Array();
下面实现GameUnit类
KeyCodeList.js//键盘点击类
var KeyCodeList = new Array();KeyCodeList.KeysDown = new Array();KeyCodeList.KeysUp = new Array();KeyCodeList.KeysDown.Put = function (keyCode) {    var retVal = KeyCodeList.KeysDown.Remove(keyCode);    KeyCodeList.KeysDown.push(keyCode);    return retVal;}KeyCodeList.KeysDown.Remove = function (keyCode) {    for (var i = 0; i < KeyCodeList.KeysDown.length; i++) {        if (KeyCodeList.KeysDown[i] == keyCode) {            KeyCodeList.KeysDown.splice(i, 1);            return true;        }    }    return false;}KeyCodeList.KeysDown.getKey = function () {    if (KeyCodeList.KeysDown.length <= 0) {        return null;    }    else {        return KeyCodeList.KeysDown[KeyCodeList.KeysDown.length - 1];    }}
AIAction.js//敌方坦克动作类
var AIAction = new Array();AIAction.takeAction = function (style, obj, seed) {    self.status = seed;    switch (style) {        case "normal":            var choose = Math.floor(seed * Math.random());            if (choose > 0 && choose <= 1) {                obj.direction = "_east";                obj.imageUrl = Resource.ROOT + Resource.IMG_TANKE_EAST;                obj.unitWidth = 31;                obj.unitHeight = 26;            }            else if (choose <= 2) {                obj.direction = "_west";                obj.imageUrl = Resource.ROOT + Resource.IMG_TANKE_WEST;                obj.unitWidth = 31;                obj.unitHeight = 26;            }            else if (choose <= 3) {                obj.direction = "_north";                obj.imageUrl = Resource.ROOT + Resource.IMG_TANKE_NORTH;                obj.unitWidth = 26;                obj.unitHeight = 31;            }            else if (choose <= 4) {                obj.direction = "_south";                obj.imageUrl = Resource.ROOT + Resource.IMG_TANKE_SOUTH;                obj.unitWidth = 26;                obj.unitHeight = 31;            }            if (choose >= 30 && choose <= 40) {                obj.tankFire("BulletComputer", 3);            }            break;        default:            break;    }}
GameUnit.js
function GameUnit(id, width, height, type) //游戏元素 —— 摆放在地图区域上的物件{    GameUnit.metaData[id] = this;    GameUnit.metaData.All.push(this);    this.id = id;    this.width = width;    this.height = height;    this.type = type;    this.initialized = false;    this.Disposed = false;    this._time = new Date().getTime(); //活动时间,被一些物件使用,例如判断连续射击等等    GameUnit.prototype._parseType = function () //初始化物件    {        this.imageUrl = GameUnit.mapImages[this.type];        switch (type) {            case "Home":                this.allowPassType = 0;                this.absorbType = Type.BulletHuman | Type.BulletComputer;                this.unitWidth = 39;                this.unitHeight = 33;                this.direction = "_north";                this.AIMode = "normal";                break;            case "TankHuman":                this.allowPassType = 0;                this.absorbType = Type.BulletComputer | Type.BulletHuman;                this.unitWidth = 29;                this.unitHeight = 32;                document.body.eventHandler = this;                document.body.onkeydown = function () {                    this.eventHandler.KeyDown(this, event);                };                document.body.onkeyup = function () {                    this.eventHandler.KeyUp(this, event);                };                this.direction = "_north";                break;            case "TankComputer":                this.allowPassType = 0;                this.absorbType = Type.BulletHuman | Type.BulletComputer;                this.unitWidth = 26;                this.unitHeight = 31;                this.direction = "_south";                this.AIMode = "normal";                break;            case "BulletHuman":                this.allowPassType = 0;                this.absorbType = Type.BulletComputer | Type.BulletHuman | Type.TankComputer | Type.Home | Type.Block;                this.unitWidth = 12;                this.unitHeight = 12;                this.repeatable = "norepeat";                this.autoDispose = true;                break;            case "BulletComputer":                this.allowPassType = 0;                this.absorbType = Type.BulletComputer | Type.BulletHuman | Type.TankHuman | Type.Home | Type.Block;                this.unitWidth = 12;                this.unitHeight = 12;                this.repeatable = "norepeat";                this.autoDispose = true;                break;            case "Block":                this.allowPassType = 0;	 //规定允许何种类型的Unit通过,0表示不允许任何单位通过                this.absorbType = Type.BulletHuman | Type.BulletComputer; //吸收(使消失)何种类型的Unit;                this.unitWidth = 16;                this.unitHeight = 16;                break;            default:                break;        }    }    GameUnit.prototype.Kill = function (obj) //销毁一个物件    {        obj.clearInterval();        obj.type = 0;        obj.dead = true;        obj.allowPassType = Type.All;        obj.absorbType = 0;        obj.KeyDown = obj.KeyUp = function () { };        obj.AIMode = 0;        obj.Draw(0, 0);        obj.whenDead();    }    this.whenDead = function () { }; //物件被销毁后触发的事件,可根据对象重写    GameUnit.prototype.KeyUp = function (sender, event) {        if (event.keyCode < 37 || event.keyCode > 40) {            if (event.keyCode == 32) {                clearInterval(this.shooterTimer);                this.shooterTimer = null;            }            return false;        }        else //处理方向键        {            KeyCodeList.KeysDown.Remove(event.keyCode);            if (KeyCodeList.KeysDown.getKey() == null) {                this.clearInterval();            }        }    }    GameUnit.prototype.KeyDown = function (sender, event) {        if (event.keyCode >= 37 && event.keyCode <= 40) //处理方向键        {            if (KeyCodeList.KeysDown.getKey() == event.keyCode) {                return;            }            KeyCodeList.KeysDown.Put(event.keyCode);            this.UnitSpeed = GameConfig.TanksSpeed;            if (event.keyCode == 37) //left            {                this.imageUrl = Resource.ROOT + Resource.IMG_TANKS_WEST;                this.unitWidth = 32;                this.unitHeight = 29;                this.direction = "_west";                this.ready(3);            }            else if (event.keyCode == 38) //up            {                this.imageUrl = Resource.ROOT + Resource.IMG_TANKS_NORTH;                this.unitWidth = 29;                this.unitHeight = 32;                this.direction = "_north";                this.ready(3);            }            else if (event.keyCode == 39) //right            {                this.imageUrl = Resource.ROOT + Resource.IMG_TANKS_EAST;                tmp = this.unitWidth;                this.unitWidth = 32;                this.unitHeight = 29;                this.direction = "_east";                this.ready(3);            }            else if (event.keyCode == 40) //down            {                this.imageUrl = Resource.ROOT + Resource.IMG_TANKS_SOUTH;                this.unitWidth = 29;                this.unitHeight = 32;                this.direction = "_south";                this.ready(3);            }        }        if (event.keyCode == 32) //space -- shoot!        {            if (this.shooterTimer == null) {                this.shooterTimer = setInterval("GameUnit.metaData." + this.id + ".humanFire()", 1);            }        }    }    GameUnit.prototype.tankFire = function (type, speed) {        if (speed == null) {            speed = GameConfig.BulletSpeed;        }        var shootTime = new Date().getTime();        //if (GameUnit.metaData["bullet" + this.id] == null || GameUnit.metaData["bullet" + this.id].dead)        if (shootTime - this._time >= 500) {            this._time = shootTime;            var bullet = new GameUnit("bullet" + shootTime, 1, 1, type);            var pos = this.getPosition();            var x = pos[0] - bullet.unitWidth / 2;            var y = pos[1] - bullet.unitHeight / 2;            bullet.direction = this.direction;            if (this.direction == "_north") {                y -= this.unitHeight / 2 + 10;            }            if (this.direction == "_south") {                y += this.unitHeight / 2 + 10;            }            if (this.direction == "_east") {                x += this.unitWidth / 2 + 10;            }            if (this.direction == "_west") {                x -= this.unitWidth / 2 + 10;            }            bullet.Draw(x, y);            bullet.ready(speed);            return;        }        else {            return;        }    }    GameUnit.prototype.humanFire = function () {        this.tankFire("BulletHuman");    }    GameUnit.prototype.setInterval = this.readyMove;    GameUnit.prototype.clearInterval = function () {        clearInterval(this.timer);        this.timer = null;        this.setInterval = this.readyMove;    }    GameUnit.prototype.readyMove = function (func) {        if (this.timer == null) this.timer = setInterval("GameUnit.metaData." + this.id + "." + func, 10);        this.setInterval = function () { };    }    GameUnit.prototype.ready = function (speed) {        if (this.timer == null) {            this.UnitSpeed = speed;            this.setInterval("Move();");        }    }    GameUnit.prototype.Move = function () {        _AI_seed = 300;        if (this.direction == null) {            throw new Error("This object cannot move!");        }        this.Test(this.posx, this.posy);        switch (this.direction) {            case "_west":                if (this.posx > 5) this.posx -= this.UnitSpeed;                else {                    _AI_seed = 100;                    if (this.autoDispose) this.Dispose();                }                break;            case "_north":                if (this.posy > 5) this.posy -= this.UnitSpeed;                else {                    _AI_seed = 100;                    if (this.autoDispose) this.Dispose();                }                break;            case "_east":                if (this.posx < GameConfig.MapWidth - this.unitWidth * this.width) this.posx += this.UnitSpeed;                else {                    _AI_seed = 100;                    if (this.autoDispose) this.Dispose();                }                break;            case "_south":                if (this.posy < GameConfig.MapHeight - this.unitHeight * this.height) this.posy += this.UnitSpeed;                else {                    _AI_seed = 100;                    if (this.autoDispose) this.Dispose();                }                break;            default:                throw new Error("Error direction to move!");        }        var region = this.Region();        if (this.meetDeal(region)) {            _AI_seed = 100;        }        var unit = this.Unit();        if (this.meetDeal(unit)) {            _AI_seed = 100;        }        this.Draw(this.posx, this.posy);        if (this.AIMode != null) {            AIAction.takeAction(this.AIMode, this, _AI_seed);        }    }    GameUnit.prototype.meetDeal = function (obj) //和物件碰撞后执行的动作    {        var killObj = false;        if (obj == null || obj.Disposed) {            return false;        }        if ((this.absorbType & Type[obj.type]) != 0) {            killObj = true;        }        if ((obj.absorbType & Type[this.type]) != 0) {            if (killObj) {                this.Kill(obj);            }            this.Kill(this);            return true;        }        if ((obj.allowPassType & Type[this.type]) == 0) {            this.Recover();            return true;        }    }    GameUnit.prototype.Draw = function (posx, posy) {        if (this.Disposed) {            return;        }        this.posx = posx;        this.posy = posy;        var foreCanvs = document.getElementById("_fore");        var newUnit = document.getElementById(this.id);        if (newUnit == null) {            newUnit = document.createElement("span");            newUnit.id = this.id;            foreCanvs.appendChild(newUnit);        }        if (this.dead) {            newUnit.style.display = 'none';        }        newUnit.style.position = "absolute";        newUnit.style.left = this.posx;        newUnit.style.top = this.posy;        newUnit.style.width = this.width * this.unitWidth;        newUnit.style.height = this.height * this.unitHeight;        if (this.repeatable == "norepeat") {            newUnit.style.backgroundRepeat = "no-repeat";        }        newUnit.style.backgroundImage = "url(" + this.imageUrl + ")";    }    GameUnit.prototype.Dispose = function () {        this.Dispose = function () { }; //该函数只能被执行一次        this.AIMode = null;        this.Move = function () { };        this.clearInterval();        var foreCanvs = $("_fore");        var unit = $(this.id);        foreCanvs.removeChild(unit);        //GameUnit.metaData[this.id] = null;        for (var i = 0; i < GameUnit.metaData.All.length; i++) {            if (GameUnit.metaData.All[i].id == this.id) {                GameUnit.metaData.All.splice(i, 1);            }        }        this.Disposed = true;    }    GameUnit.prototype.Region = function () //返回当前所在的区域    {        for (var i = 0; i < Region.metaData.All.length; i++) {            if (this == Region.metaData.All[i]) {                continue;            }            if (this.Intersects(Region.metaData.All[i])) {                return Region.metaData.All[i];            }        }        return null;    }    GameUnit.prototype.Unit = function () //返回当前遇到的物件    {        for (var i = 0; i < GameUnit.metaData.All.length; i++) {            if (this == GameUnit.metaData.All[i]) {                continue;            }            if (this.Intersects(GameUnit.metaData.All[i])) {                return GameUnit.metaData.All[i];            }        }        return null;    }    GameUnit.prototype.Overlaped = function () //判断是否和其他物件重叠    {        if (this.posx == null || this.posy == null) {            return false;        }        for (var i = 0; i < GameUnit.metaData.All.length; i++) {            if (this == GameUnit.metaData.All[i] || GameUnit.metaData.All[i].posx == null || GameUnit.metaData.All[i].posy == null) {                continue;            }            if (this.Intersects(GameUnit.metaData.All[i])) {                return true;            }        }        return false;    }    this._parseType();} GameUnit.prototype = new BaseUnit();GameUnit.mapImages = new Array();GameUnit.mapImages["TankHuman"] = Resource.ROOT + Resource.IMG_TANKS_NORTH;GameUnit.mapImages["TankComputer"] = Resource.ROOT + Resource.IMG_TANKE_SOUTH;GameUnit.mapImages["BulletHuman"] = Resource.ROOT + Resource.IMG_BULLET;GameUnit.mapImages["BulletComputer"] = Resource.ROOT + Resource.IMG_BULLET;GameUnit.mapImages["Home"] = Resource.ROOT + Resource.IMG_HOME;GameUnit.mapImages["Block"] = Resource.ROOT + Resource.IMG_SBLOCK;GameUnit.metaData = new Array();GameUnit.metaData.All = new Array();
所有类实现玩后,就可以生成页面了
坦克大战
地图可以任意修改,赋附件:

王川
2014/02/14

转载地址:http://ddvvi.baihongyu.com/

你可能感兴趣的文章
web.py 0.3 新手指南 - 如何用Gmail发送邮件
查看>>
web.py 0.3 新手指南 - RESTful doctesting using app.request
查看>>
Mysql中下划线问题
查看>>
Xcode 11 报错,提示libstdc++.6 缺失,解决方案
查看>>
idea的安装以及简单使用
查看>>
Windows mysql 安装
查看>>
python循环语句与C语言的区别
查看>>
vue 项目中图片选择路径位置static 或 assets区别
查看>>
vue项目打包后无法运行报错空白页面
查看>>
Vue 解决部署到服务器后或者build之后Element UI图标不显示问题(404错误)
查看>>
element-ui全局自定义主题
查看>>
facebook库runtime.js
查看>>
vue2.* 中 使用socket.io
查看>>
openlayers安装引用
查看>>
js报错显示subString/subStr is not a function
查看>>
高德地图js API实现鼠标悬浮于点标记时弹出信息窗体显示详情,点击点标记放大地图操作
查看>>
初始化VUE项目报错
查看>>
vue项目使用安装sass
查看>>
HTTP和HttpServletRequest 要点
查看>>
在osg场景中使用GLSL语言——一个例子
查看>>