第六季-常用编程框架和算法

  • 01.MVC架构
    • MVC的含义
  • 02.单例模式
    • 单例(instance)的特点
    • 作用
    • 实现
    • 调用
    • 结果
    • 补充
  • 03.观察者模式-订阅发布模式
    • 流程
    • 实现
    • 结果
  • 04.工厂模式
    • 特点和作用
    • 实现
  • 05.代理模式
    • 代理 (额外的控制器)
  • 06.递归寻路
    • 题目
    • 步骤
    • 实战
      • 先制作格子 NodeGrid.ts
      • 制作地图
      • 寻路(完整代码 FindPath.ts)
      • 运行结果
    • 不足
  • 07.A星寻路
    • 步骤
    • 实现
  • 08.对象池模式
    • 意义
    • 流程图
    • 直接创建对象
    • 使用对象池
    • 运行效果

01.MVC架构

是一种设计程序的思路/套路.

MVC的含义

  1. M-Model模型(数据)
    FlappyBird中小鸟的位置,当前激活的节点等等
  2. V-View视觉层
    展示出来的界面,通常是引擎进行处理
  3. C-Controller控制器(逻辑)
    写下的代码,小鸟碰到管子判断游戏结束

总结内涵:拿数据-根据逻辑-刷新界面

02.单例模式

单例(instance)的特点

  1. 单例类,全局只有一个对象,不可能产生多个
  2. 在代码任意位置容易获取这个对象

作用

防止一个全局使用的类频繁的实现与销毁
控制实例数目,节省系统资源

实现

/**单例类 */
export default class InstanceDemo {/**全局唯一的对象 */private static _instance: InstanceDemo = null; //private的变量名前一般加个_/**获取单例对象 */public static getInstance(): InstanceDemo{if (InstanceDemo._instance == null){InstanceDemo._instance = new InstanceDemo();}return InstanceDemo._instance;}/**防止创建第二个对象 */constructor(){if (InstanceDemo._instance != null) {throw Error("Already has InstanceDemo._instance");}}num: number = 0;trace(){this.num ++;console.log("trace",this.num);}
}

调用

import InstanceDemo from "./instanceDemo";const {ccclass, property} = cc._decorator;@ccclass
export default class HelloWorld extends cc.Component {start () {/**获取单例对象 */let instance = InstanceDemo.getInstance();/**调用单例方法 */instance.trace();//创建对象new InstanceDemo();}// update (dt) {}
}

结果

补充

实现单例模式,将构造函数私有化就好了

/**私有化构造函数*/
private constructor(){}

03.观察者模式-订阅发布模式

流程

  1. A订阅了开始游戏事件
  2. B抛出(发布)了开始游戏事件
  3. A响应事件

特点:A不管什么时候出发,只负责触发时接受,B不知道谁注册了事件,只负责触发.

实现

将WebStorm改为ES6:File→Setting→Languages&Framework→JavaScript→选择ES6
还要将typescriptconfig.json里的“ES5”改为“ES6”

EventCenter.ts 事件控制中心
EventHandler 记录事件信息

/**观察者模式 */
export default class EventCenter {//事件数据 存放事件名和注册该事件的注册信息们private static events: Map<string,Array<EventHandler>> = new Map<string,Array<EventHandler>>();/**注册事件* eventName: string 事件名* target: object 谁注册的事件 用于回调绑定* callBack: Function 回调*/static registerEvent(eventName: string,target: object,callBack: Function): void {if (eventName == undefined || target == undefined || callBack == undefined) {throw Error("regsiter event error");}/**判断是否已经有该事件被注册过 */if (EventCenter.events[eventName] == undefined){EventCenter.events[eventName] = new Array<EventHandler>();}/**将此次注册事件的信息存入Map中 */let handler = new EventHandler(target,callBack);EventCenter.events[eventName].push(handler);}/**触发事件* eventName: string 事件名* param?: any 回调参数*/static postEvent(eventName: string,param?: any): void {let handlers = EventCenter.events[eventName];if (handlers == undefined){return;}//遍历所有注册了该事件的eventHandlerfor (let i = 0; i < handlers.length; i++){let handler = handlers[i];if (handler){//调用事件回调//使用try-catch防止回调有报错但没有信息try {//.call(绑定的this,参数) 调用方法handler.function.call(handler.target,param);}catch (e){console.log(e.message);console.log(e.stack.toString()); //输出堆栈信息}}}}/**移除注册事件* eventName: string 事件名* target: object 谁注册的事件 用于回调绑定* callBack: Function 注册事件回调*/static removeEvent(eventName: string,target: object,callBack: Function): void {if (eventName == undefined || target == undefined || callBack == undefined) {throw Error("destory event failed");}let handlers = EventCenter.events[eventName];if (handlers){for (let i = 0; i < handlers.length; i++){let handler = handlers[i];if (handler && target == handler.target && callBack == handler.function){//有两种移除方法 "= undefined"性能要好 "splice"要省内存空间handlers[i] = undefined;// handlers.splice(i,1);break;}}}}}/**注册信息类 */
class EventHandler {/**记录谁注册了事件 */target: object;/**记录事件触发时调用的方法 */function: Function;constructor(target: object,func: Function){this.target = target;this.function = func;}
}

Panel.ts 用来注册事件

import EventCenter from "./EventCenter";/**用来注册事件 检验观察者模式 */
const {ccclass, property} = cc._decorator;@ccclass
export default class Panel extends cc.Component {@property(cc.Label)label: cc.Label = null;onLoad () {EventCenter.registerEvent("gameStart",this,this.onGameStart);//5s后移除事件注册this.scheduleOnce(function() {this.onDestroy();}.bind(this),5)}/**注册事件回调 */onGameStart(str: string){console.log("event callBack");this.label.string = str;}onDestroy(){//移除gameStart事件console.log("remove event gameStart");EventCenter.removeEvent("gameStart",this,this.onGameStart);}
}

触发事件

EventCenter.postEvent("gameStart","game is start!");

结果


点击按钮


输出

注意:target节点destory后,一定要记得注销其注册的事件。否则callBack会出错

04.工厂模式

特点和作用

操作的对象本身只实现功能方法,具体的操作由工厂实现
这样不会暴露对象及创建逻辑

实现

/**工厂模式 *///c: {new ():T} 告诉ide这个T类型的c是可以被实例化的
export function createAttack<T extends IActor>(c: {new (): T},life: number): T {let object = new c();object.attack();object.life = life;return object;
}export function createDie<T extends IActor>(c: {new (): T}): T {let object = new c();object.die();return object;
}/**角色接口 */
interface IActor {attack: Function;die: Function;life: number;
}/**盗贼 */
export class Thief implements IActor {life: number;attack() {console.log("thief attack");}die() {console.log("thief is die");}
}/**战士 */
export class Warrior implements IActor {life: number;attack() {console.log("warrior attack");}die() {console.log("warrior is die");}
}

调用

 createAttack<Thief>(Thief,10);createDie<Warrior>(Warrior);

05.代理模式

代理 (额外的控制器)

  1. 单一原则,不给一个类太多功能.
    自己的功能留在类里面,将一些逻辑控制放到外面.
    高内聚,低耦合.
  2. 不方便访问一个类的时候,给个代理
    例如:明星和经纪人的关系;
    快递中发货人-快递-收货人;
    cc.loader.load(…)内部非常复杂,但对于调用的人来说并不关 心内部逻辑.

06.递归寻路

题目

需要从①走到②,要怎么走(可以斜着走,褐色是不能走的)

步骤

  1. 把起点当作当前节点
  2. 重复以下步骤
    a. 把当前节点加入openList,标记Open
    b.查找可以走的下一步节点
    c.把下一步可以走的节点排序
    d.把下一步可以走的离终点最近的点,当做当前的寻路节点
    e.把走过的节点标记为Close
  3. 直到查找到目标节点

实战

先制作格子 NodeGrid.ts

import FindPath from "./FindPath";/**寻路地图格子 */
const {ccclass, property} = cc._decorator;
/**格子 显示层 */
@ccclass
export default class NodeGrid extends cc.Component {dataGrid: DataGrid = null;findPathController: FindPath;onLoad () {this.node.on(cc.Node.EventType.TOUCH_END,this.onBtnGrid,this);}/**点击格子 确定起点终点 生成路线 */onBtnGrid(){this.findPathController.onTouch(this);}/**刷新格子颜色 */updateGridColor(){if (this.dataGrid.type == GrideType.Normal){this.node.color = new cc.Color().fromHEX("#fffff9");} else if (this.dataGrid.type == GrideType.Wall){this.node.color = new cc.Color().fromHEX("#151513");} else if (this.dataGrid.type == GrideType.Road){this.node.color = new cc.Color().fromHEX("#41ff0b");} else {this.node.color = new cc.Color().fromHEX("#fff42d");} }
}/**格子数据 数据层 */
export class DataGrid {type: GrideType;//坐标x: number;y: number;/**是否为当前节点 */inOpenList: boolean = false;/**路径节点标记 */inCloseList: boolean = false;
}/**格子类型枚举 */
export enum GrideType {Normal, //普通Wall, //墙Start, //起点,当前节点End, //终点Road, //路线
}

在场景下创建一个40x40的格子,并挂载NodeGrid.ts

制作地图

/**随机生成8x8地图 */generateMap () {for (let x = 0;x < 8;x ++){this.dataGrids[x] = [];this.nodeGrids[x] = [];for (let y = 0;y < 8;y ++){let rand = Math.random();let grideType: GrideType = GrideType.Normal;if (rand < 0.2) { //1/5的概率生成墙grideType = GrideType.Wall;}//数据层let grid: DataGrid = new DataGrid();grid.x = x;grid.y = y;grid.type = grideType;this.dataGrids[x][y] = grid;//视图层let gridNode: NodeGrid = cc.instantiate(this.nodeGridPrefab).getComponent(NodeGrid);gridNode.node.position = cc.v3(50 * (x - 4),50 * (y - 4),0);this.nodeGrids[x][y] = gridNode;gridNode.dataGrid = grid;gridNode.findPathController = this;gridNode.updateGridColor();gridNode.node.parent = this.node;}}}

寻路(完整代码 FindPath.ts)

import NodeGrid, { DataGrid, GrideType } from "./NodeGrid";/**递归寻路 */
const {ccclass, property} = cc._decorator;@ccclass
export default class NewClass extends cc.Component {/**格子节点 */@property(cc.Node)nodeGridPrefab: cc.Node = null;dataGrids: DataGrid[][] = [];nodeGrids: NodeGrid[][] = [];/**记录起点 */startGrid: DataGrid = null;/**记录终点 */endGrid: DataGrid = null;onLoad () {this.generateMap();}/**随机生成8x8地图 */generateMap () {for (let x = 0;x < 8;x ++){this.dataGrids[x] = [];this.nodeGrids[x] = [];for (let y = 0;y < 8;y ++){let rand = Math.random();let grideType: GrideType = GrideType.Normal;if (rand < 0.2) { //1/5的概率生成墙grideType = GrideType.Wall;}//数据层let grid: DataGrid = new DataGrid();grid.x = x;grid.y = y;grid.type = grideType;this.dataGrids[x][y] = grid;//视图层let gridNode: NodeGrid = cc.instantiate(this.nodeGridPrefab).getComponent(NodeGrid);gridNode.node.position = cc.v3(50 * (x - 4),50 * (y - 4),0);this.nodeGrids[x][y] = gridNode;gridNode.dataGrid = grid;gridNode.findPathController = this;gridNode.updateGridColor();gridNode.node.parent = this.node;}}}/**点击格子 */onTouch(nodeGrid: NodeGrid){if (!this.startGrid) { //设置起点this.startGrid = nodeGrid.dataGrid;this.startGrid.type = GrideType.Start;nodeGrid.updateGridColor();}else if (!this.endGrid) { //设置终点this.endGrid = nodeGrid.dataGrid;this.endGrid.type = GrideType.End;nodeGrid.updateGridColor();//寻路this.startFindPath();}}openPath: DataGrid[] = [];/**寻路 */startFindPath(){if (this.find(this.startGrid)) {for (let i = 0; i < this.openPath.length; i++) {let path = this.openPath[i];path.type = GrideType.Road;this.nodeGrids[path.x][path.y].updateGridColor();}}else {console.log("无法走到终点");}}find(base: DataGrid) {this.openPath.push(base);base.inOpenList = true;if (base == this.endGrid){ //寻路结束return true;}let round = this.getRoundGrid(base);for (let i = 0;i < round.length;i ++) {let nextBaseGride = round[i];if (this.find(nextBaseGride)) {return true;}}base.inCloseList = true;this.openPath.splice(this.openPath.length - 1,1);return false;}/**获取当前节点周围可走的节点 */getRoundGrid(grid: DataGrid): DataGrid[] {let arr: DataGrid[] = [];//周围的格子this.addToRoundIfNeed(arr,this.getGrid(grid.x,grid.y + 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y));this.addToRoundIfNeed(arr,this.getGrid(grid.x,grid.y - 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y));this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y - 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y + 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y - 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y + 1));//会将数组里元素两两进行比较,自定义方法里返回-1就不交换位置 返回1交换位置arr.sort(this.compareGrids.bind(this));return arr;}/**将格子放到数组里 */addToRoundIfNeed(arr: DataGrid[],roundGrid: DataGrid) {//当前节点和路径节点都不计入if (!roundGrid || roundGrid.type == GrideType.Wall || roundGrid.inCloseList || roundGrid.inOpenList){return;}if (roundGrid) {arr.push(roundGrid);}}/**根据坐标获取格子数据 */getGrid(x: number,y: number): DataGrid {//边界判断if (x < 0 || x >= 8 || y < 0 || y >=8){return null;}return this.dataGrids[x][y];}/**格子比较和终点的距离* 距离小的放在前面*/compareGrids(grid0: DataGrid,grid1: DataGrid): number{let grid0Dis = this.getDistance(grid0);let grid1Dis = this.getDistance(grid1);if (grid0Dis > grid1Dis) {return 1;}else{return -1;}}/**获取节点到终点距离 */getDistance(grid: DataGrid){return Math.abs(grid.x - this.endGrid.x) + Math.abs(grid.y - this.endGrid.y);}/**点击重新开始*/onBtnRestart(){for (let x = 0;x < this.dataGrids.length;x ++){for (let y = 0;y < this.dataGrids[x].length;y ++){let dataGrid = this.dataGrids[x][y];dataGrid.inOpenList = false;dataGrid.inCloseList = false;if (dataGrid.type != GrideType.Wall){dataGrid.type = GrideType.Normal;}this.nodeGrids[x][y].updateGridColor();}}this.startGrid = null;this.endGrid = null;this.openPath = [];}}

运行结果

不足

当前算法只是寻找下一步的最优解,并不是全局的最优解,有时并不是最优路径。

07.A星寻路

可以看下大神写的A星寻路算法原理

步骤

  1. 把起点当做当前节点
  2. 重复以下步骤
    a.把当前节点加入openList,标记Open
    b.查找可以走的下一步节点
    c.把下一步可以走的节点的父节点,设置为当前节点
    d.把下一步可以走的节点加入到openList,排序
    e.把openList中的第一个节点,当做当前节点
    f.把走过的节点标记为Close
  3. 直到查找到目标节点

实现

DataGrid添加一个字段

/**父节点 用于A星寻路 */
fatherGrid: DataGrid = null;

FindPathAX.ts完整代码

/**A星寻路 */
import NodeGrid, { DataGrid, GrideType } from "./NodeGrid";const {ccclass, property} = cc._decorator;@ccclass
export default class FindPathAX extends cc.Component {/**格子节点 */@property(cc.Node)nodeGridPrefab: cc.Node = null;dataGrids: DataGrid[][] = [];nodeGrids: NodeGrid[][] = [];/**记录起点 */startGrid: DataGrid = null;/**记录终点 */endGrid: DataGrid = null;onLoad () {this.generateMap();}/**随机生成8x8地图 */generateMap () {for (let x = 0;x < 8;x ++){this.dataGrids[x] = [];this.nodeGrids[x] = [];for (let y = 0;y < 8;y ++){let rand = Math.random();let grideType: GrideType = GrideType.Normal;if (rand < 0.2) { //1/5的概率生成墙grideType = GrideType.Wall;}//数据层let grid: DataGrid = new DataGrid();grid.x = x;grid.y = y;grid.type = grideType;this.dataGrids[x][y] = grid;//视图层let gridNode: NodeGrid = cc.instantiate(this.nodeGridPrefab).getComponent(NodeGrid);gridNode.node.position = cc.v3(50 * (x - 4),50 * (y - 4),0);this.nodeGrids[x][y] = gridNode;gridNode.dataGrid = grid;gridNode.findPathController = this;gridNode.updateGridColor();gridNode.node.parent = this.node;}}}/**点击格子 */onTouch(nodeGrid: NodeGrid){if (!this.startGrid) { //设置起点this.startGrid = nodeGrid.dataGrid;this.startGrid.type = GrideType.Start;nodeGrid.updateGridColor();}else if (!this.endGrid) { //设置终点this.endGrid = nodeGrid.dataGrid;this.endGrid.type = GrideType.End;nodeGrid.updateGridColor();//寻路this.startFindPathAStar();}}/**待考虑的节点列表 */openPath: DataGrid[] = [];/**A星寻路 */startFindPathAStar(){this.openPath.push(this.startGrid);this.startGrid.inOpenList = true;while (this.openPath.length > 0) {let current = this.openPath.shift(); //shift--取出数组中首个元素if (current == this.endGrid) {break;}let round = this.getRoundGrid(current);for (let i = 0;i < round.length;i ++) {let r = round[i];r.fatherGrid = current;r.inOpenList = true;}this.openPath = this.openPath.concat(round); //拼接数组this.openPath.sort(this.compareGridsAStar.bind(this));current.inCloseList = true;}if (this.endGrid.fatherGrid) {let pathGrid = this.endGrid;while (pathGrid) {pathGrid.type == GrideType.Road;this.nodeGrids[pathGrid.x][pathGrid.y].updateGridColor();pathGrid = pathGrid.fatherGrid;}}else {console.log("没有路径可走");}}/**获取当前节点周围可走的节点 */getRoundGrid(grid: DataGrid): DataGrid[] {let arr: DataGrid[] = [];//周围的格子this.addToRoundIfNeed(arr,this.getGrid(grid.x,grid.y + 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y));this.addToRoundIfNeed(arr,this.getGrid(grid.x,grid.y - 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y));this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y - 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y + 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y - 1));this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y + 1));//会将数组里元素两两进行比较,自定义方法里返回-1就不交换位置 返回1交换位置arr.sort(this.compareGridsAStar.bind(this));return arr;}/**将格子放到数组里 */addToRoundIfNeed(arr: DataGrid[],roundGrid: DataGrid) {//当前节点和路径节点都不计入if (!roundGrid || roundGrid.type == GrideType.Wall || roundGrid.inCloseList || roundGrid.inOpenList){return;}if (roundGrid) {arr.push(roundGrid);}}/**根据坐标获取格子数据 */getGrid(x: number,y: number): DataGrid {//边界判断if (x < 0 || x >= 8 || y < 0 || y >=8){return null;}return this.dataGrids[x][y];}/**格子排序 优化* 距离小的放在前面*/compareGridsAStar(grid0: DataGrid,grid1: DataGrid): number{let grid0Dis = this.getDistanceAStar(grid0,this.startGrid,this.endGrid);let grid1Dis = this.getDistanceAStar(grid1,this.startGrid,this.endGrid);if (grid0Dis > grid1Dis) {return 1;}else{return -1;}}/**获取综合距离 优化* grid 当前节点* start 起始节点* end 目标节点*/getDistanceAStar(grid: DataGrid,start: DataGrid,end: DataGrid) {let endDis = Math.abs(grid.x - end.x) + Math.abs(grid.y - end.y);let startDis = Math.abs(grid.x - start.x) + Math.abs(grid.y - start.y);return endDis + startDis;}/**点击重新开始*/onBtnRestart(){for (let x = 0;x < this.dataGrids.length;x ++){for (let y = 0;y < this.dataGrids[x].length;y ++){let dataGrid = this.dataGrids[x][y];dataGrid.inOpenList = false;dataGrid.inCloseList = false;if (dataGrid.type != GrideType.Wall){dataGrid.type = GrideType.Normal;}this.nodeGrids[x][y].updateGridColor();}}this.startGrid = null;this.endGrid = null;this.openPath = [];}}

08.对象池模式

意义

用于解决当需要创建大量相同对象的时候,避免重复创建,节能.

流程图

直接创建对象

/**对象池模式 */
const {ccclass, property} = cc._decorator;@ccclass
export default class PoolDemo extends cc.Component {@property(cc.Node)nodeIcon: cc.Node = null;onLoad () {}shoot() {let node = cc.instantiate(this.nodeIcon);//创建完节点还要从父节点上移出,太费事了node.runAction(cc.sequence(cc.moveBy(1,0,300),cc.removeSelf()));node.parent = this.node;}update() {this.shoot();}
}

会不断创建节点,并将节点从父节点移出,耗费性能,内存.

使用对象池

/**对象池模式 */
const {ccclass, property} = cc._decorator;@ccclass
export default class PoolDemo extends cc.Component {@property(cc.Node)nodeIcon: cc.Node = null;/**对象池 */pool: cc.Node[] = [];onLoad () {}shoot() {let node = this.getNode();//创建完节点还要从父节点上移出,太费事了node.runAction(cc.sequence(cc.moveBy(1,0,300),cc.removeSelf(),cc.callFunc(function () {node.position = cc.Vec2.ZERO; //节点位置重置//用完后将节点放回对象池this.pool.push(node);}.bind(this))));node.parent = this.node;}/**获取节点* 如果对象池里有节点的话就取出来用* 没有的话就实例化一个*/getNode(): cc.Node {if (this.pool.length > 0) {return this.pool.shift();}else {console.log("创建了一个节点");return cc.instantiate(this.nodeIcon);}}update() {this.shoot();}
}

运行效果

只需创建了62个节点

零基础学CocosCreator·第六季-常用编程框架和算法相关推荐

  1. 零基础学CocosCreator·第七季-制作一款塔防游戏

    第七季-制作一款塔防游戏 01.塔防前言 为什么是塔防? 准备 02.使用TileMap创建地图 新建地图 获取地图 编辑地图 代码操控 运行 03-16.实战中 04.状态机 代码 08.事件分发器 ...

  2. 零基础学python尹会生_编程零基础应当如何开始学习 Python?

    这是一篇小白自学Python的经验,针对没有任何经验.从零开始学习Python的童鞋,不管你是出于兴趣,还是想提升工作效率,或者想要转行,都可以作为一个参考. 作为过来人,先分享几点建议给你: 1.找 ...

  3. 零基础学Arcgis(六)|空间数据采集与管理(3)数据编辑

    写在前面的话: B站搜索"中图地信"便可观看全套71章节详细操作视频(有操作数据获取,同步学习) (一)使用要素模板 1.使用要素模板 [1]启动ArcMap,新建空白地图文档: ...

  4. 零基础学SQL(六、数据的增删改查简单语句)

    目录 前置建表 一.数据的插入 1.插入数据语法 1.1.插入完整的行 1.2.插入行的一部分 1.3 .插入多行 1.4.插入某些查询的结果 二.数据的简单查询 1.查询数据语法 三.数据的修改 1 ...

  5. 零基础学Java语言--第6周编程题

    1单词长度(5分) 题目内容: 你的程序要读入一行文本,其中以空格分隔为若干个单词,以'.'结束.你要输出这行文本中每个单词的长度.这里的单词与语言无关,可以包括各种符号,比如"it's&q ...

  6. 做大数据用java还是python_新手零基础学做大数据工程师,编程学Java还是Python比较好?...

    Python和Java,是大数据行业最常见的两种编程语言,对于想转行大数据的人人来说,学习哪个语言是比较好的选择呢? Python Python和大数据: Python本身的特点是高效率的开发和简单的 ...

  7. 零基础学Arcgis系列教程

    教程地址:点击查看 零基础学Arcgis(七)|空间数据采集与管理(4)数据检查 (一)创建地理数据库拓扑 1.创建地理数- 零基础学Arcgis(六)|空间数据采集与管理(3)数据编辑 (一)使用要 ...

  8. 零基础学FPGA(八):可编程逻辑单元(基本结构,Xilinx+Altera)

    目录 日常·唠嗑 一.概述 二.基于多路选择器的逻辑单元 1.基于多路选择器的逻辑单元(早期) 2.基于PLD结构的逻辑单元(类CPLD) 3.基于查询表的逻辑单元(目前主流) 三.Xilinx基本结 ...

  9. 【零基础学Java】—throw关键字(四十六)

    [零基础学Java]-throw关键字(四十六) 一.throw关键字 public class demo {public static void main(String[] args) {//创建i ...

最新文章

  1. Ubuntu 中python 升级到3 后apt-get 一直报错
  2. MYSQL主从同步故障一例及解决过程
  3. PLSQL_性能优化系列20_Oracle Result Cash结果缓存
  4. 【要闻】Kubernetes无用论诞生、Elasticsearch 7.6.2 发布
  5. 京东笔试4.2-19:00随笔
  6. LeetCode 206 Reverse Linked List 解题报告
  7. Ruby 获取 HTTP 回应的编码和内容
  8. py2exe支持python3.6_使用Py2Exe for Python3创建自己的exe程序示例
  9. HALCON: 本地程序函数(.hdev或.dev)、HDevelop函数文件或外部函数(.hdvp)及库函数(.hdpl)使用详解
  10. PC电脑版微信聊天记录迁移方法
  11. 练习:随机点名器案例
  12. 码支付易支付等接口对接方法
  13. NYIST468(Miller_Rabin+定理)
  14. iPhone 13 支持卫星上网?没那么简单
  15. 浅谈 Java 中的枚举
  16. pip显示网络不可达错误解决
  17. pccad无法找到所需的动态链接库_请教天正给排水8.2问题(tch_initstart.arx 无法找到所需的动态链接库或其他文件)...
  18. 多个Serial.print拼成一条Serial.print省时间吗?
  19. ArcGIS基础实验操作100例--实验61数据框投影变换
  20. unity手游使用terrian注意事项

热门文章

  1. 多线程面试题(高薪高频)
  2. ENVI中的辐射校正
  3. SSM框架解决QQ邮箱激活535 Error: ÇëʹÓÃÊÚȨÂëµÇ¼¡£ÏêÇéÇë¿´及端口25被占用问题
  4. 以业务管理信息化系统建设推动施工企业数字化转型
  5. 河内塔问题(Hanoi Tower)
  6. 2019年度个人计划
  7. python监控文件或目录变化
  8. HDI板和盲埋孔电路板的区别
  9. Python3快速入门教程-zyiz.net
  10. loadrunner使用sitescope监测监控mysql数据库