前段时间微信朋友圈里微信经常向我推塔防小游戏,一时心血来潮,研究了下这类小游戏的开发设计,仿造了经典塔防游戏《Field Runner》。文章在其它平台发表过,为了让更多同学可以看到,作者借助 Chat 平台发表。

本场 Chat 中,读者可以了解到以下内容:

  1. 了解到塔防小游戏的一些设计思路;
  2. 游戏地图的实现思路;
  3. 敌人以及敌人自动寻路的实现思路;
  4. 防御塔的设计实现思路;
  5. 子弹自动攻击的实现思路;
  6. 游戏信息的设计。

前段时间我也不知道哪来的勇气,竟然雄心勃勃要设计一款拯救泡泡鱼的小游戏,梦想着流量暴涨的美好远景,动手设计实现了好几种玩法,找了批朋友玩了下,原本满怀期望却被吐槽声淹没了。沉寂了一段时间,有天突然发现微信朋友圈里一直出现塔防小游戏的广告,心血来潮,要不也弄个塔防游戏试试。这次我学乖了,网上找了个现成的塔防小游戏叫《Field Runner》,决定按照它的思路重新实现了一遍。

本篇文章通过讲解 copy 一款塔防游戏的经历,讲解里也包含着一些设计思路,希望能帮到想要开发小游戏的爱好者们。游戏一些代码以及设计思想借鉴了其它作者的,我指出这个只是想告诉大家学习别人的作品是游戏入门的捷径。

游戏参考图:

说明

  1. 文章里代码示例基于 egret 游戏引擎,我会尽量减少使用引擎 api 带来的阅读影响。若仍不是很清楚,请访问egret 引擎官方网站。
  2. 讲解代码使用的语言是 typescript,代码中 egret 对象是由游戏引擎提供。
  3. 文章里的代码只是示例,只保留了关键代码,不能保证正确运行。

设计

记得大学期间经常喜欢跟室友一起玩塔防游戏,这个游戏里玩家建造各种防御塔抵御敌人一批一批的进攻,守护好基地。 每次开局看到大批敌人进攻,我就拼命的造防御塔,不把屏幕填满无法抚慰我不安的心。

我们回头看下上面的游戏场景,一群敌人士兵从左边出发进攻基地,玩家在地图各个位置几乎满屏放置了大量的格林机枪炮塔,拦截打击阻止敌人进攻趋势,期间有敌人不断爆炸死亡。游戏上一栏是金币、分数、游戏轮次、玩家(基地)血量,下一栏是暂停、快进、武器种类。

接下来我们一步一步的设计这款单机游戏。

开始

每次开发小游戏,我喜欢先在游戏场景里添加一张背景图片,刷新后看到图片是一件开心的事情,而且我还可以调整 UI 显示适配。

// Main.tsclass Main extends eui.UILayer {    //代码省略...    // 在游戏里添加一个背景    // Map 类是自定义的一个地图类    protected createGameScene(): void {        this.addChild(new Map());    }}

地图的实现思路

接下来开始实现地图 Map 类了。根据上面的游戏场景,我需要在地图上标记好几个位置,首先想到了代码写死坐标的方式(代码示例):

// Map.ts// 说明:以下坐标随意写class Map extends egret.DisplayObjectContainer {    //代码省略...    // 敌人起始坐标    public static startPoint:[number, number] = [1, 11];    // 地图高度和宽度    public static tileWidth:number = 800;    public static tileHeight:number = 500;    // 地图瓦片数,格子个数 行 20,列 10    public static mapWidth:number = 20;    public static mapHeight:number = 10;    // 武器选择栏坐标,当前就定义一种武器类型    public static weaponPoinit:[number, number] = [100, 111];    // 暂停按钮    public static stopPoinit:[number, number] = [10, 111];    // 显示金币位置    public static moneyPoinit:[number, number] = [10, 10];    // 显示分数位置    public static scorePoinit:[number, number] = [30, 10];    // 显示玩家生命值    public static lifePoinit:[number, number] = [80, 10];    // 代码省略... }

这些坐标代表的含义已经很清楚了,然后将各个坐标上添加对应的图标。比如我现在添加一个武器选择的一个图标(代码示例):

// Weapon.tsprivate onAddToStage() {  this.gatingdIcon = this.createBitmapByName("gatingdIcon_png");  this.gatingdIcon.x = Map.weaponPoinit[0];  this.gatingdIcon.y = Map.weaponPoinit[1];  this.gatingdIcon.width = 100;  this.gatingdIcon.height = 100;  // 将图标添加到父层上  this.parent.addChild(this.gatingdIcon);}

这种代码写死坐标的方式简单,但是需要去调整位置,如果以后换地图,还得重新调整计算各个精灵的位置,不利于扩展。后面我改用 Tiled map,这是个 2d 地图编辑器,具体可查看官方地址。在 tiled 编辑器上我可以随心标记各个图标的坐标信息并命名,然后游戏里加载导出的地图文件后,可直接根据名称查找坐标,还可以设计各种复杂的地形。

限于篇幅,地图编辑器的使用我这边不做分享,网上有很多教程,用起来非常简单。

导出的地图文件需要开发者自己解析,不过 egret 引擎提供了第三方解析库,我只要按照引擎文档简单修改下就可引入使用。
在 Map 类中,我对外提供了 getMapObj 方法,用来获取 tiled 地图上我设计好的标记。该方法有两个参数,第一个表示标记所属的类别名称,第二个表示标记名称(代码示例):

// Map.tsclass Map extends egret.DisplayObjectContainer {    public static tmxTileMap: tiled.TMXTilemap;    public static getMapObj(parentName:string, targetName: string) {        let toolMap:any = Map.tmxTileMap.getChildByName(parentName);        let childrens = toolMap._childrens || [];        let targetObj;        childrens.map(child => {            if (child.$name == targetName) {                targetObj = child;            }        });        return targetObj;    }}

还是用武器选择的图标来说明,在 tiled 地图中,我创建了一个叫'tool'的父层(类别),然后在这个层下创建了一个叫'gatingdIcon'的层。
接下来在场景里添加武器选择图标(代码示例):

// Weapon.tsprivate onAddToStage() {  const tagetMap = Map.getMapObj('tool', 'gatingdIcon');  if (tagetMap) {      this.gatingdIcon = this.createBitmapByName("gatingdIcon_png");      this.gatingdIcon.x = targetMap.$x;      this.gatingdIcon.y = targetMap.$y;      this.gatingdIcon.width = targetMap.$width;      this.gatingdIcon.height = targetMap.$height;      // 将图标添加到父层上, this.parent 我外部传入的      this.parent.addChild(this.gatingdIcon);  }}

其它图标的坐标信息也按照上面来获取,不再复述。

在游戏里,敌人士兵行走区域以及武器放置区域有范围限制,比如地图边界、障碍物。Tiled 导出的是瓦片地图,就是说地图有两种坐标可以表示游戏元素的位置,瓦片格子坐标和像素 XY 坐标。

武器放置区域的设计思路

刚刚讲过,地图是被分割成一个个小方块(瓦片)的,武器放置的地方应该就是 1 个格子(因为我的武器就一个格子大),那我只需要获取当前武器移动时所在的地图格子坐标即可判断是否放置。

获取手指(或鼠标)所在的格子坐标(代码示例):

// x 和 y,表示当前手指(或鼠标)的坐标private getAvailablePositionNearby(pointX:number, pointY:number) {    // tx,ty 表示格子坐标,我就四舍五入了    const tx = Math.round(pointX - startPointX / tileWidth);    const tx = Math.round(pointY - startPointY / tileHeight);    // x,y 表示实际格子的像素坐标(相对舞台)    const x = startPointX + tx * tileWidth;    const y = startPointY + ty * tileHeight;    return {x: x, y: y, tx: tx, ty: ty};}

通过上面获取到武器当前放置的坐标后,直接赋值给当前拖动的武器(代码示例):

// 放置武器private placeWeapon(weapon:Gatling) {    const point = this.getAvailablePositionNearby(pointX, pointY);    if (point) {        this.dragWeapon.x = point.x;        this.dragWeapon.y = point.y;        this.dragWeapon.tx = point.tx;        this.dragWeapon.ty = point.ty;    }}

但前面说过,实际地图上有很多地方是不允许放置武器的,比如地图边界、障碍物、该格子已经放置了武器、无道路等这些情况都需要判断(代码示例):

private allowBoolean(point) {    let bool = false;    if (point) {        if (          (point.tx==0 && point.ty==1) ||          point.tx < 0 ||          point.tx > mapWidth ||          point.ty < mapHeight ||          // 这个格子玩家已经放置武器了          this.player.getWeaponAt(point.tx, point.ty) ||          // 这个格子不在敌人可行军道路上(寻路算法生成)          !this.player.buildPath(point.tx, point.ty)        ) {            bool = false;        } else {            bool = true;        }    }    return bool;}

敌人士兵行走区域的设计思路

先思考下敌人的行走路线:敌人是从起始地一波一波的发起进攻,行军过程中,遇到武器塔防得绕道,不能超出地图边界,到玩家基地后消失。故编辑地图时,标记好敌人的起始地(出生地)位置、玩家基地位置。

代码里获取位置(代码示例):

private setPoint() {    const startMap = Map.getMapObj('soldierBirthPool', 'pointStart');    const endMap = Map.getMapObj('soldierBirthPool', 'pointEnd');    this.startPoint = [parseInt(startMap.$x, 10), parseInt(startMap.$y, 10)];    this.endPoint = [parseInt(endMap.$x, 10), parseInt(endMap.$y, 10)];}

刚刚说过,敌人行走是从一个点到另一个点,中间行军路线会因遇到武器塔防而改变,这个一般通过寻路算法来实现。
要实现寻路算法,依赖一种数据结构-图。游戏中背景图并没有存在清晰的道路规划,故可以将整个地图分割成一个一个的小格子,在某一个格子上的敌人行军,只能往上下左右四个方向在格子中移动。图是由顶点和边组成的,所以把每个格子看成一个顶点,两个相邻格子之间,连两条有向边,边的权值为 1。寻找最接近最短路径的路线,使用 Astar 算法来实现,同时计算顶点之间的距离的启发式函数,这里使用曼哈顿距离(两个点之间横纵坐标的距离和),计算简单耗时少。

曼哈顿距离计算(示例代码):

private manhattan(start, end) {    return Math.abs(start.x - end.x) + Math.abs(start.y - end.y);}

了解了这方面知识的后,并非要去实现 A*算法,这样开发成本划不来。游戏中我就引入了现成的 Astar 算法库,根据上面所述,启发式函数选择了曼哈顿距离计算,接下来就是构造图了。

由于地图实际上已经被分割成格子(瓦片),故简单处理即可。这里需要说明下,游戏里得给每个敌人士兵设计好路线,而且随着玩家在地图上添加武器防御塔,以及不断前进后,路线需要实时更新,直到敌人不在地图上才无需更新。

生成路线集合(代码示例):

// 生成路径坐标集合public buildPath(tx?, ty?, start?, end?) {    // 图    let map = [];    // 当前敌人起始坐标    let s = start;    // 当前敌人终点坐标    let e = end;    if (!s || s[0] < 0) {        s = this.startPoint;    }    if (!end) {        e = this.endPoint;    }    // 遍历地图格子,生成集合    for (let i = 0; i < this.mapHeight; i++) {        map[i] = [];        for (let j = 0; j < this.mapWidth; j++) {            let hasWeaponAt = this.getWeaponAt(j, i);            // 说明:遍历地图,若发现该格子上有武器防御塔,那么设置为 1,否则设置为 0 。即 1 表示该格子敌人不能走            map[i][j] = hasWeaponAt ? 1 : 0;        }    }    // 若外面传入指定区域,该区域值设置为 1, 无法行走    if (tx || ty) {       map[ty][tx]= 1;    }    // Astar 算法的使用,查找最接近优解的路径集合    let pathArr = Astar.findPath(map, s, e) || [];    return pathArr;}

通过调用 buildPath 方法,设置好士兵的路径后,随着游戏帧率的变动,不断更改士兵的像素坐标,士兵就往前移动了。在生成图的原理讲过,在某一个格子上的敌人行军,只能往上下左右四个方向在格子中移动。敌人移动(坐标变动)过程中,当走完所在的格子时,得判断接下来行走的方向(上下左右),其实就是遍历敌人的路径(瓦片)集合,通过找到当前行走所在的格子位置,找到下一个临近的格子。

查找敌人下一个行走的格子,即行走方向(代码示例):

 // solider 士兵  private getNextDirection(solider:Soldier) {   for(let i = 0; i < solider.path.length - 1; i++) {       let path = solider.path[i];       // 查找到当前士兵行走的格子位置,这样就可以确定下个格子的位置 i+1       if (path[0] == solider.tx && path[1] == solider.ty) {           // next:下一个行走的格子(瓦片)           let next = solider.path[i+1];           return [next[0]-solider.tx, next[1]-solider.ty];       }   }   return null; }

敌人士兵获取到的行军路线,是一个个格子坐标集合,实际士兵不可能从一个格子里瞬移到下一个格子,士兵是有行军速度的,为了达到平滑的行军效果,需要先判断敌人是否已走完所在格子,然后根据行军速度和行军方位设置敌人 xy 坐标。

首先定义好士兵的行军方向表示,当行军方向改变,士兵行军动画方向也随着改变(代码示例):

// Soldier.ts// direction[0]值表示士兵左右方向行军,direction[0] === 1 右边行军,direction[0] === -1 左边行军// direction[1]值表示士兵左右方向行军,direction[1] === 1 下边行军,direction[1] === -1 上边行军// 等于 0 表示停止行军public direction: [number, number] = [1,0];// avatar 角色 它可以代表角色的位置、方向、运动状态和姿势public avatar: egret.MovieClip;// 士兵的行军速度public speed:number = 0:private setDirection() {    // 代码省略...    if (this.direction[0] == 1) {      // 往右边行军,播放对应的动画,gotoAndPlay 是 egret 的播放帧动画的方法      this.avatar.gotoAndPlay("solider_walk_right", -1);    }    // 代码省略...}

根据获取的行军方向值乘以行军速度,坐标值累加设置士兵的 xy 坐标(代码示例):

// Player.ts// 根据移动方位设置敌人士兵的 xy 坐标private moveByDirection(solider:Soldier) {   if (!solider.direction) {       return;   }   if (solider.direction[0] != 0) {       solider.x += solider.speed * solider.direction[0];   } else   if (solider.direction[1] != 0) {       solider.y += solider.speed * solider.direction[1];   }}// 士兵移动private moveSoldier(solider:Soldier) {    // direction[0] !=0 表示目标在 x 轴方向移动    if (solider.direction[0] != 0) {        // 格子是否走完        let dx = target.x - ( this.startPoint[0] + tile[0] *  this.tileWidth );        if (dx === 0) {            // 根据移动方位设置敌人士兵的 xy 坐标            solider.setDirection(this.getNextDirection(target));        }    }    // 代码省略...}

上面 direction[0] 需要说明下,由于敌人行军的格子之间是相邻的(看上面数据结构图的解释),故敌人要么左右方向移动,要么上下方向移动,不会出现 xy 轴同时移动的情况。

至此,敌人士兵行走区域的设计实现已讲完。这块设计的几个主要点:

  1. 分析敌人行走策略,利用 Astar 寻路算法获取行军路线;
  2. 将地图分割成格子,构造寻路算法所需的数据图;
  3. 设计敌人的行走方式,行军方向获取方式通过计算下一个行走格子瓦片坐标减去当前行走格子瓦片坐标,即 [next[0]-solider.tx, next[1]-solider.ty];
  4. 敌人平滑行军的实现设计;

武器防御塔的实现思路

地图设计好后,接下来开始设计在地图上添加武器防御塔。为了提高游戏趣味,游戏里设计了多种武器供玩家选择,本文就讲解机关炮武器的设计思路。机关炮大家都比较熟悉,是一种能连续自动射击的武器,而且一般固定在一个底盘上。地图设计章节讲过,游戏里士兵上下左右四个方向可行走,那么机关炮的底盘也要支持 360 度旋转,而且武器旋转角度是跟着士兵移动而改变的。

首先在地图右下角的武器栏,添加图标(示例代码):

private onAddToStage() {    this.gatingdIcon = this.createBitmapByName("gatingdIcon_png");    const targetMap = Map.getMapObj('tool', 'gatingdIcon');    if (targetMap) {        this.gatingdIcon.x = targetMap.$x;        this.gatingdIcon.y = targetMap.$y;        this.gatingdIcon.width = targetMap.$width;        this.gatingdIcon.height = targetMap.$height;    }    this.parent.addChild(this.gatingdIcon);}

再添加触摸监听事件(示例代码):

private onAddToStage() {    // 省略代码...    // 移动    this.stage.addEventListener(egret.TouchEvent.TOUCH_MOVE, this.touchMoveHandler, this);    // 开始(按下 -down)    this.stage.addEventListener(egret.TouchEvent.TOUCH_BEGIN, this.touchBeginHandler, this);    // 结束(离开 -up)    this.stage.addEventListener(egret.TouchEvent.TOUCH_END, this.touchEndHandler, this);}

选择图标后,就会触发移动武器的效果,放开后将武器添加到地图上,放置武器位置实现在上节的'武器放置区域的设计思路'里已讲解过。

添加好武器后,接下来设计武器的属性。

攻击力

最简单的就是给武器设置一个恒定的攻击力:

public damage:number = 10;

然后士兵受到攻击后,生命值减去伤害值:

// 士兵中弹public getShot(damage:number) {    // 中弹后扣掉生命值    this.health -= damage;}

现实中士兵中弹后所受伤害严重程度不是固定的,游戏中通过设计武器浮动攻击力来模拟实现,设置一个最大伤害,再设置一个最小伤害,随机取值:

private maxDamage: number = 20:private minDamage: number = 10;// 随机获取攻击力值public getDamange() {    return Math.round(Math.random()*(this.maxDamage - this.minDamage)) + this.minDamage;}

攻击范围

先给武器赋予固定的攻击范围:

public attackRadius: nunmber = 200;

然后检测是否有敌人进入攻击范围:

要判断敌人是否进入武器的攻击范围,在平面直角坐标系中计算出武器和士兵的距离,然后距离值跟攻击范围相比,距离计算的数学公式如下:

实现检测函数(示例代码):

public isInAttackRadius(solider: Solider) {    // 计算武器和士兵的距离(在平面直角坐标系中用两点间距离公式)    const dx = solider.x - this.x;    const dy = solider.y - this.y;    const distance = Math.sqrt(dx * dx + dy * dy);    return distance <= this.attackRadius;}

子弹攻击到敌人的判断

之前讲过,游戏里武器底盘是随着敌人移动,可以 360 度自动旋转的。当敌人进入攻击范围后,武器自动旋转指向敌人,然后开火。

武器开火流程如下:

击中敌人也是有时间延迟的,为了模拟该场景,开火时间大于某个毫秒值后表示击中敌人了,开火后时间变量重置为 0,同时重置武器转动时间。

部分实现(示例代码):

// 检测是否击中,加了个 300ms 的判断,模拟private checkShot() {    if (this.fireTime > 300) {        this.fireTime = 0;        this.turnTime = new Date().getTime();        return true;    }    return false;}// public hit(soldier: Soldier) {    let status;    // 武器跟士兵的角度,武器默认指向左边(+180)    const angle = (180 / Math.PI) * Math.atan2(dy, dx) + 180;    // 帧,假设每帧等于 10 个角度    const frame = Math.round(angle/10);    const hitBool = this.isInAttackRadius(soldier);    this.status = status;    this.currentAngleFrame = frame;    if (hitBool) {        if (this.status === 'idle') {            this.trunTime = new Date().getTime();            this.fireTime = 0;        } else {            this.fireTime = new Date().getTime() - this.trunTime;        }        return this.checkShot();    }    return false;}

说明:角度的计算通过 atan2 函数,并非 atan 函数,atan 函数对(y/x)、(-y/-x)是没办法区分的,算出来的角度不是实际的值。

武器升级

游戏中,玩家可以通过花费一定的游戏币升级武器,升级越高所花费的游戏币越多, 升级后武器的各项属性也随着提高(示例代码):

// 一般武器等级是有限制的private canUpgrade() {    return this.level < 8;}public upgrade() {    if (!this.canUpgrade()) {        return false;    }    this.level ++;    this.cost += 20;    this.minDamage += 12;    // 代码省略...}

当玩家点击某个武器升级按钮后,就从游戏币里扣除当前武器的升级所需的价钱。

其它

每个武器都是自动追踪一个敌人士兵的,代码要做些简单修改,在玩家类里,需要做些简单的判断,自动给武器赋予目标:

public autoAttack() {    // 省略代码...    if (weapon.solider === null       || !weapon.checkInAttackRadius(solider)      || (weapon.solider.x >= (this.stage.stageWidth + weapon.width))    ) {        weapon.solider = this.findSolider(weapon);    }}

敌人士兵的实现思路

士兵主要就几个点:行军路线、行军速度、生命值、生命条、死亡爆金币、等级提升。
士兵的部分实现其实跟武器差不了多少,其最主要的设计难点就是行军,本文已在地图章节做了详细讲解。故关于士兵的设计不再做详细介绍了。

受到伤害后血条的变化:

public getShot(damage:number) {    // 中弹后扣掉生命值    this.health -= damage;    // 当扣除生命值小于 0 时,生命值重新设置为 0.(伤害大小不一定)    if (this.health < 0) {        this.health = 0;    }    const percent = this.health / this.maxHealth;    // 更新生命血条,四舍五入,默认血条长度为 60    let healthWidth = Math.round(60 * percent);    // this.healthBar 士兵的血条图标    if (this.healthBar) {        this.healthBar.width = healthWidth;    }}

游戏积分、游戏币、轮次、结束

积分游戏币

public autoAttack() {    if (solider.isDead()) {        // 积分        this.score += solider.score;        // 游戏币        this.money += solider.money;    }}

轮次

轮次就是每轮士兵都出现后,轮次加一。

结束

结束就是玩家生命值为零的时候。

当敌人进攻到基地后,玩家扣除生命值:

public autoAttack() {    // 省略代码...    if (solider.x >= this.stage.stageWidth + solider.width) {        this.life-- ;    }}

玩家类的设计

上面所有的设计,都是在玩家类整合的,实际文章已经讲解了大量的例子,留给大家设计吧。

总结

游戏的设计围绕玩家类,总结里就画了个图,希望能帮助理解思路:

阅读全文: http://gitbook.cn/gitchat/activity/5e0407dbf60753276dbde0f0

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App , GitChat 专享技术内容哦。

塔防小游戏的开发设计经历相关推荐

  1. 塔防小游戏的开发设计

    来由 前段时间我也不知道哪来的勇气,竟然雄心勃勃要设计一款拯救泡泡鱼的小游戏,梦想着流量暴涨的美好远景,动手设计实现了好几种玩法,找了批朋友玩了下,原本满怀期望却被吐槽声淹没了.沉寂了一段时间,有天突 ...

  2. python3小游戏源代码_Python入门塔防小游戏,开发步骤和源码,带你轻松学python...

    在上一篇文章中,我介绍了有关python入门塔防小游戏的一些功能,下面我继续讲解有关这个小游戏的大致开发步骤. 开发工具还是: Python版本:3.6.4 相关的模块有:pygame模块,以及一些p ...

  3. android 小游戏源码_Python入门太难?不如从玩塔防小游戏开始,玩通关就能学会编程...

    我一直认为,在python入门阶段学习基础理论,太枯燥.所以我们整理了很多有关python的项目案例,有详细教程还有源码,希望能帮助更多对python感兴趣的人. 这是其中一个适合入门的Python项 ...

  4. canvas塔防小游戏-保卫大司马

    项目地址: http://codeape.site:8181/#/protectTheHorse b站地址: https://www.bilibili.com/video/bv1xZ4y1v7ju 基 ...

  5. python 玩公众号游戏_Python入门太难?不如从玩塔防小游戏开始,玩通关就能学会编程...

    我一直认为,在python入门阶段学习基础理论,太枯燥.所以我们整理了很多有关python的项目案例,有详细教程还有源码,希望能帮助更多对python感兴趣的人. 这是其中一个适合入门的Python项 ...

  6. 年会活跃微信群小游戏有哪些?塔防小游戏经典玩法讲解

    马上接近年底了,很多企业都在准备办一个适合当下环境的年会活动,现在仍是疫情防御阶段,那该如何筹备一个线上活动呢?经调查发现,不少公司会选择TOM小游戏作为一个活动的项目,哪选择什么样的游戏适合呢?一起 ...

  7. 微信塔防小游戏开发教程,唤境引擎制作塔防游戏分享

    今天带来的是塔防游戏制作攻略! 点击这里来下载工程文件, 点击这里可以下载工程中所用的素材哦~ 预览状态时敌人会从四个生成点随机生成,并且会自动寻路绕过黑色墙体走向红色终点.点击黑色墙体可以创建炮塔, ...

  8. Unity3D游戏开发——塔防小游戏

    游戏效果图: 本文参照siki学院的塔防游戏教程写的.http://www.sikiedu.com,搜索塔防游戏登入即可观看. 游戏流程: 1.首先创建cube调整其大小(以便计算),然后将其设成预设 ...

  9. python小游戏——塔防小游戏代码开源

    ♥️作者:小刘在这里 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的,绽放,愿所有的美好,再疫情结束后如约而至. 目录 一.效 ...

最新文章

  1. python面试题目
  2. 混合商业模式解决方案-----多种电子商务模式的融合与创新
  3. CSS3实现带阴影的弹球
  4. [机器学习]梯度提升决策树--GBDT
  5. 【Android开发】之Fragment与Acitvity通信
  6. 各种主流Linux操作系统概况
  7. sql注入空格被过滤_网站渗透:SQL注入与WAF绕过思路分享
  8. rabbitmq AmqpClient 使用Fanout 交换机投递与接收消息,C++代码示例
  9. 弱性能穿戴设备App化之Lua For STM32
  10. 02 Apache Solr: 概览 Solr在信息系统架构中的位置
  11. 3、用一个div模拟textarea的实现
  12. java实习简历_怎么样写一份比较好的Java实习生的简历?
  13. 信息学奥赛 java C 选_信息学奥赛(NOIP)入门指南(新家长必读)
  14. Ubuntu 11.10ibus万能五笔
  15. 远程桌面技术之-“你的凭据不工作”
  16. mysql校对集_MySQL校对集问题
  17. 2022年跨境电商卖家如何在Facebook上做广告【完整指南】
  18. JS常用的输出内容的方式详解(5种输出方式)
  19. 约瑟夫环问题的几种解法
  20. 关于carsim2016破解问题

热门文章

  1. siege 用户登录_性能测试之siege
  2. Eclipse各个版本及其对应代号列表
  3. 108个搞笑经典短句(转)
  4. 07-sketch-符号组件
  5. 用 Docker 现代化你的传统企业!|航海日志 Vol.22
  6. scala--快速了解Breeze
  7. FPGA设计篇之双调排序
  8. 习惯重于方法 - 书籍
  9. 【概率论】二维随机变量:联合分布律、边缘分布律和条件分布律之间的关系
  10. python怎样安装词云_在python中怎样安装词云-女性时尚流行美容健康娱乐mv-ida网...