【Tank】4.0 位置管理、代码调优、砖墙模型渲染
2022-05-12 本文已影响0人
bobokaka
本篇章最终文件结构如下:
![](https://img.haomeiwen.com/i16102290/4c446d3a24bffdcc.png)
模型位置管理服务
![](https://img.haomeiwen.com/i16102290/0f9af2d66e5f086a.png)
暂时设定为最顶层为敌方坦克出生区域,底部为我方老巢和我方坦克出生区域。
为达到这样的效果,修改src/canvas/abstract/AbstractCanvas.ts优化如下:
import config from "../../config";
/**
* 抽象类
*/
export default abstract class AbstractCanvas {
// 元素
protected atom = []
//构造函数渲染
constructor(
protected app = document.querySelector('#app') as HTMLDivElement,
// @ts-ignore
protected el = document.createElement<HTMLCanvasElement>('canvas')!,
// @ts-ignore
protected canvas = el.getContext('2d')!
) {
this.createCanvas()
}
// 抽象方法:渲染贴图
abstract render(): void
// 初始化canvas
protected createCanvas() {
// 元素的宽高就是全局canvas得到宽高
// @ts-ignore
this.el.width = config.canvas.width
// @ts-ignore
this.el.height = config.canvas.height
// 测试画布
// 定义填充颜色
// this.canvas.fillStyle = '#16a085'
// 绘制矩形
// this.canvas.fillRect(0, 0, config.canvas.width, config.canvas.height)
// 最终元素要放到我们的app的div中
// @ts-ignore
this.app.insertAdjacentElement('afterbegin', this.el)
}
// 绘制模型
// protected:子类可以调用,外部不能调用
//num: 渲染多少个数量
//model: 模型
protected drawModels(num: number, model: ConstructorModel) {
this.positionCollection(num).forEach((position) => {
const instance = new model(this.canvas, position.x, position.y)
instance.render()//渲染贴图
// this.canvas.drawImage(
// image.get('straw')!,
// position.x,
// position.y,
// config.model.straw.width,
// config.model.straw.height
// );
})
// Array(num).fill('').forEach(() => {
// const position = this.position()
// this.canvas.drawImage(
// image.get('straw')!,
// position.x,
// position.y,
// config.model.straw.width,
// config.model.straw.height
// );
// })
// const img = document.createElement('img')
// img.src = imgUrl;
// //图片是异步加载,所以需要将图片加载完毕后,才进行渲染绘制
// img.onload = () => {
// const position = this.position()
// this.canvas.drawImage(img, position.x, position.y, config.model.straw.width, config.model.straw.height);
// }
}
protected positionCollection(num: number) {
const collection = [] as { x: number, y: number }[]
for (let i = 0; i < num; i++) {
let count = 0
while (true) {
const position = this.position()
const exists = collection.some(item =>
item.x == position.x && item.y == position.y)
if (!exists || count > 4000) {
collection.push(position)
break;
}
// 防止死循环
count++;
}
}
return collection
}
// 返回随机位置
protected position() {
debugger
let x: number, y: number;
let gridNumX = config.canvas.width / config.model.straw.width;
// 随机格子数量
let leftNumX = Math.floor(Math.random() * gridNumX)
//转换成px
x = leftNumX * config.model.straw.width
let gridNumY = config.canvas.height / config.model.straw.height;
// 随机格子数量
let leftNumY = Math.floor(Math.random() * (gridNumY - 3))
//转换成px,且顶部空出一格
y = (leftNumY +1) * config.model.straw.height
return {x, y}
}
}
坐标的位置其他代码中也要使用,这时候可以剥离出来,也就是说不完全服务于我们的“草地画布”,可能其他画布要用,或者模型要用。
新建src/service/position.ts
/**
* 服务
* 位置生成
*/
import config from "../config";
class Position {
public getPositionCollection(num: number) {
const collection = [] as { x: number, y: number }[]
for (let i = 0; i < num; i++) {
let count = 0
while (true) {
const position = this.position()
const exists = collection.some(item =>
item.x == position.x && item.y == position.y)
if (!exists || count > 4000) {
collection.push(position)
break;
}
// 防止死循环
count++;
}
}
return collection
}
// 返回随机位置
public position() {
debugger
let x: number, y: number;
let gridNumX = config.canvas.width / config.model.straw.width;
// 随机格子数量
let leftNumX = Math.floor(Math.random() * gridNumX)
//转换成px
x = leftNumX * config.model.straw.width
let gridNumY = config.canvas.height / config.model.straw.height;
// 随机格子数量
let leftNumY = Math.floor(Math.random() * (gridNumY - 3))
//转换成px,且顶部空出一格
y = (leftNumY + 1) * config.model.straw.height
return {x, y}
}
}
export default new Position()
修改src/canvas/abstract/AbstractCanvas.ts
import config from "../../config";
import position from "../../service/position";
/**
* 抽象类
*/
export default abstract class AbstractCanvas {
// 元素
protected atom = []
//构造函数渲染
constructor(
protected app = document.querySelector('#app') as HTMLDivElement,
// @ts-ignore
protected el = document.createElement<HTMLCanvasElement>('canvas')!,
// @ts-ignore
protected canvas = el.getContext('2d')!
) {
this.createCanvas()
}
// 抽象方法:渲染贴图
abstract render(): void
// 初始化canvas
protected createCanvas() {
// 元素的宽高就是全局canvas得到宽高
// @ts-ignore
this.el.width = config.canvas.width
// @ts-ignore
this.el.height = config.canvas.height
// 最终元素要放到我们的app的div中
// @ts-ignore
this.app.insertAdjacentElement('afterbegin', this.el)
}
// 绘制模型
// protected:子类可以调用,外部不能调用
//num: 渲染多少个数量
//model: 模型
protected drawModels(num: number, model: ConstructorModel) {
position.getPositionCollection(num).forEach((position) => {
const instance = new model(this.canvas, position.x, position.y)
instance.render()//渲染贴图
})
}
}
防止不同模型重叠
src/service/position.ts
/**
* 服务
* 位置生成
*/
import config from "../config";
type positionType = { x: number, y: number }
class Position {
// 集合包括砖墙、草地、砖块,都在里面
collection: positionType[] = []
public getPositionCollection(num: number) {
const collection = [] as { x: number, y: number }[]
for (let i = 0; i < num; i++) {
let count = 0
while (true) {
const position = this.position()
// 从整个集合中防止重叠坐标
const exists = this.collection.some(item =>
item.x == position.x && item.y == position.y)
if (!exists || count > 4000) {
collection.push(position)
this.collection.push(position)
break;
}
// 防止死循环
count++;
}
}
return collection
}
// 返回随机位置
public position() {
debugger
let x: number, y: number;
let gridNumX = config.canvas.width / config.model.straw.width;
// 随机格子数量
let leftNumX = Math.floor(Math.random() * gridNumX)
//转换成px
x = leftNumX * config.model.straw.width
let gridNumY = config.canvas.height / config.model.straw.height;
// 随机格子数量
let leftNumY = Math.floor(Math.random() * (gridNumY - 3))
//转换成px,且顶部空出一格
y = (leftNumY + 1) * config.model.straw.height
return {x, y}
}
}
export default new Position()
画布重绘不断新建实例的bug修复
这里有一个画布的bug,在我们不断地重绘过程中,会不断的生成,也就是说,比如草地,会不断的变换位置。
![](https://img.haomeiwen.com/i16102290/50fa23b1f89888bd.png)
首先将实例新建和渲染到画布分成2步骤。
src/canvas/abstract/AbstractCanvas.ts
import config from "../../config";
import position from "../../service/position";
/**
* 抽象类
*/
export default abstract class AbstractCanvas {
// 元素实例
protected models: IModel[] = []
//构造函数渲染
constructor(
protected app = document.querySelector('#app') as HTMLDivElement,
// @ts-ignore
protected el = document.createElement<HTMLCanvasElement>('canvas')!,
// @ts-ignore
protected canvas = el.getContext('2d')!
) {
this.createCanvas()
}
// 抽象方法:渲染贴图
abstract render(): void
// 初始化canvas
protected createCanvas() {
// 元素的宽高就是全局canvas得到宽高
// @ts-ignore
this.el.width = config.canvas.width
// @ts-ignore
this.el.height = config.canvas.height
// 测试画布
// 定义填充颜色
// this.canvas.fillStyle = '#16a085'
// 绘制矩形
// this.canvas.fillRect(0, 0, config.canvas.width, config.canvas.height)
// 最终元素要放到我们的app的div中
// @ts-ignore
this.app.insertAdjacentElement('afterbegin', this.el)
}
// 绘制模型,生成模型实例,只负责创建实例
// protected:子类可以调用,外部不能调用
//num: 渲染多少个数量
//model: 模型
protected createModels(num: number, model: ConstructorModel) {
position.getPositionCollection(num).forEach((position) => {
const instance = new model(this.canvas, position.x, position.y)
this.models.push(instance)
// this.canvas.drawImage(
// image.get('straw')!,
// position.x,
// position.y,
// config.model.straw.width,
// config.model.straw.height
// );
})
// Array(num).fill('').forEach(() => {
// const position = this.position()
// this.canvas.drawImage(
// image.get('straw')!,
// position.x,
// position.y,
// config.model.straw.width,
// config.model.straw.height
// );
// })
// const img = document.createElement('img')
// img.src = imgUrl;
// //图片是异步加载,所以需要将图片加载完毕后,才进行渲染绘制
// img.onload = () => {
// const position = this.position()
// this.canvas.drawImage(img, position.x, position.y, config.model.straw.width, config.model.straw.height);
// }
}
// 画布渲染模型(将模型渲染到画布上)
protected renderModels() {
this.models.forEach(model => model.render())
}
}
src/canvas/Straw.ts
/**
* 画布
* 草地
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelStraw from '../model/Straw'
class Straw extends AbstractCanvas {
// 构造函数,初始时run一次
constructor() {
super();
// super:调用父类的方法
super.createModels(config.straw.num, ModelStraw)
}
render(): void {
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
}
// 草地在一个图层,所以只需要new一个实例即可。
export default new Straw()
src/model/abstract/AbstractModel.ts
import {image} from "../../service/image";
import config from "../../config";
/**
* 抽象类
*/
export default abstract class AbstractModel {
//构造函数渲染
constructor(
protected canvas: CanvasRenderingContext2D,
protected x: number,
protected y: number
) {
// this.render()
}
// 抽象方法:渲染贴图
abstract render(): void
// 渲染函数
protected draw() {
debugger
this.canvas.drawImage(
image.get("straw")!,
this.x,
this.y,
config.model.straw.width,
config.model.straw.height
)
}
}
修改src/vite-env.d.ts
/// <reference types="vite/client" />
/**
* 全局声明
*/
/**
* 模型对象
*/
interface ConstructorModel {
new(canvas: CanvasRenderingContext2D,
x: number,
y: number): any
}
/**
* 模型实现的函数、方法
*/
interface IModel {
render(): void
}
将src/model/ModelStraw.ts重命名src/model/Straw.ts
运行:
![](https://img.haomeiwen.com/i16102290/eb3ba219688a654f.png)
砖墙
同草地模型。
新建src/canvas/Wall.ts
/**
* 画布
* 墙
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelWall from '../model/Wall'
class Wall extends AbstractCanvas {
// 构造函数,初始时run一次
constructor() {
super();
// super:调用父类的方法
super.createModels(config.wall.num, ModelWall)
}
render(): void {
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
}
// 墙在一个图层,所以只需要new一个实例即可。
export default new Wall()
修改src/model/abstract/AbstractModel.ts
import config from "../../config";
/**
* 抽象类
*/
export default abstract class AbstractModel {
//构造函数渲染
constructor(
protected canvas: CanvasRenderingContext2D,
protected x: number,
protected y: number
) {
}
// 抽象方法:渲染贴图
abstract render(): void
// 渲染函数
protected draw(img: HTMLImageElement) {
debugger
this.canvas.drawImage(
img,
this.x,
this.y,
config.model.straw.width,
config.model.straw.height
)
}
}
修改src/model/Straw.ts
/**
* 模型
* 草地
*/
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
export default class ModelStraw extends AbstractModel implements IModel {
// 继承父类抽象方法:渲染贴图
// 一些初始化自定义的动作、行为,都在这里进行
render(): void {
super.draw(image.get("straw")!)
}
}
新建src/model/Wall.ts
/**
* 模型
* 草地
*/
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
export default class ModelWall extends AbstractModel implements IModel {
// 继承父类抽象方法:渲染贴图
// 一些初始化自定义的动作、行为,都在这里进行
render(): void {
super.draw(image.get("brickWall")!)
}
}
修改配置文件
// 草地
import imgUrlStraw from './static/images/straw/straw.png'
// 砖墙
import imgUrlBrickWall from './static/images/wall/wall.gif'
import imgUrlTankTop from './static/images/tank/top.gif'
export default {
// 画布
canvas: {
width: 900,
height: 600,
},
// 模型
model: {
// 草地
straw: {
width: 30,
height: 30,
}
},
straw: {
num: 100,
},
wall: {
num: 100,
},
// 图片
images: {
// 草地
straw: imgUrlStraw,
brickWall: imgUrlBrickWall,
tank: imgUrlTankTop
}
}
这时候,只需要在主函数中声明渲染即可。
src/main.ts
import config from './config'
import './style.scss'
import canvasStraw from './canvas/Straw'
import canvasWall from './canvas/Wall'
import {promises} from "./service/image";
const app = document.querySelector<HTMLDivElement>("#app")
// @ts-ignore
app.style.width = config.canvas.width + 'px'
// @ts-ignore
app.style.height = config.canvas.height + 'px'
const bootstrap = async () => {
// console.log(promises)
//先加载各种贴图
await Promise.all(promises)
// console.log(image.get('straw'))
// 调用render方法渲染
canvasStraw.render()
canvasWall.render()
}
void bootstrap()
效果如下:
![](https://img.haomeiwen.com/i16102290/2512d7e92e56b2c0.png)
此时html中相当于创建了2个画布,并且块级元素堆砌:
![](https://img.haomeiwen.com/i16102290/52ed29c5de6a78b9.png)
因此需要调整scss:
src/style.scss
body {
background-color: #000;
//视图的宽度
width: 100vw;
//视图的高度
height: 100vh;
display: flex;
/*主轴*/
justify-content: center;
/*交叉轴*/
align-items: center;
//div画布默认就是居中
#app {
background-color: #333;
position: relative;
canvas {
position: absolute;
}
}
}
![](https://img.haomeiwen.com/i16102290/74fbf3c8dc85699c.png)