【2.0 tank】坦克大战项目初始化、设计思想、编程思想
创建项目
npm create vite
Ok to proceed? (y) y
? Project name: » tank
> vanilla
> vanilla-ts
通过vscode或者webstorm打开项目,并运行:
npm install
本篇章最终的文件结构:
package.json
{
"name": "tank",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"sass": "^1.51.0",
"typescript": "^4.5.4",
"vite": "^2.9.7"
}
}
开发思路
类似于ps的图层,不同的图层叠加,形成一张图片。
不同的图层管理不同的内容,比如:
一张图层管理草地;
一张图层管理墙;
一张图层管理敌方坦克;
一张图层管理我方坦克;
然后,就是控制多久将某个图层重新画一次。
画草地
首先需要初始化一下样式。
当然不需要自己造轮子。
打开https://www.bootcdn.cn/
将minireset.min.css弄到项目下。
修改index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<link href="./src/style/minireset.min.css" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
全局配置项src/config.ts
export default {
canvas: {
width: 900,
height: 600,
}
}
修改src/main.ts
import config from './config'
import './style.scss'
const app = document.querySelector<HTMLDivElement>("#app")
// @ts-ignore
app.style.width = config.canvas.width + 'px'
// @ts-ignore
app.style.height = config.canvas.height + 'px'
修改src/style.scss
body {
background-color: #000;
//视图的宽度
width: 100vw;
//视图的高度
height: 100vh;
display: flex;
/*主轴*/
justify-content: center;
/*交叉轴*/
align-items: center;
//div画布默认就是居中
#app {
background-color: red;
}
}
画布abstract抽象类
首先准备图片,比如草地。
src/static/images/straw/straw.png
新建草地图层。草地在一个图层,所以只需要new一个实例即可。
src/canvas/straw.ts
import config from "../config";
class straw {
//构造函数渲染
constructor(
protected app = document.querySelector('#app') as HTMLDivElement,
// @ts-ignore
protected el = document.createElement<HTMLCanvasElement>('canvas')!,
// @ts-ignore
protected canvas = el.getContext('2d')!
) {
// 元素的宽高就是全局canvas得到宽高
// @ts-ignore
el.width = config.canvas.width
// @ts-ignore
el.height = config.canvas.height
//最终元素要放到我们的app的div中
// @ts-ignore
app.insertAdjacentElement('afterbegin',el)
}
}
// 草地在一个图层,所以只需要new一个实例即可。
export default new straw()
引入src/main.ts
import config from './config'
import './style.scss'
import './canvas/straw'
const app = document.querySelector<HTMLDivElement>("#app")
// @ts-ignore
app.style.width = config.canvas.width + 'px'
// @ts-ignore
app.style.height = config.canvas.height + 'px'
其实无论是草地、砖墙、还是坦克,都是类似的构造和创建类,因此可以将方法抽象如下:
新建src/canvas/abstract/canvas.ts
import config from "../../config";
/**
* 抽象类
*/
export default abstract class canvasAbstract{
//构造函数渲染
constructor(
protected app = document.querySelector('#app') as HTMLDivElement,
// @ts-ignore
protected el = document.createElement<HTMLCanvasElement>('canvas')!,
// @ts-ignore
protected canvas = el.getContext('2d')!
) {
// 元素的宽高就是全局canvas得到宽高
// @ts-ignore
el.width = config.canvas.width
// @ts-ignore
el.height = config.canvas.height
//最终元素要放到我们的app的div中
// @ts-ignore
app.insertAdjacentElement('afterbegin',el)
}
}
修改src/canvas/straw.ts
import canvasAbstract from "./abstract/canvas";
class straw extends canvasAbstract{
}
// 草地在一个图层,所以只需要new一个实例即可。
export default new straw()
新建src/canvas/tank.ts
import canvasAbstract from "./abstract/canvas";
class tank extends canvasAbstract{
}
// 坦克在一个图层,所以只需要new一个实例即可。
export default new tank()
效果一致,代码达到复用状态。
将构造函数优化代码。
src/canvas/abstract/canvas.ts
import config from "../../config";
/**
* 抽象类
*/
export default abstract class canvasAbstract {
//构造函数渲染
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()
}
// 初始化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)
}
}
为了多个画布叠加,就需要像图层一样,画布透明叠加。
模型
比如,坦克的画布有了。
那需要准备需要敌方坦克,可能需要20,30个坦克,每一个坦克都是一个对象,
每一个坦克对象需要记录很多属性,包括但不限于坐标、方向、血量。
每一个坦克对象不能有重叠。
坦克还可以被打爆。
所以,就需要有一个容器,来记录坦克。
我们有4个地方可以放置坦克数量。第一个是配置工具类src/config.ts,另一个就是src/canvas/tank.ts,画布,因为每一局坦克画布是唯一的;第三个地方是抽象类,相当于每一个画布中都记录有多少个元素。第四个,单独建立一个配置文件存储。
这里我们通过画布抽象类进行初始化。
src/canvas/abstract/canvas.ts
import config from "../../config";
/**
* 抽象类
*/
export default abstract class canvasAbstract {
// 元素
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()
}
// 初始化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)
}
}
在画布中渲染模型
修改src/canvas/abstract/canvas.ts
import config from "../../config";
import imgUrl from '../../static/images/straw/straw.png'
/**
* 抽象类
*/
export default abstract class canvasAbstract {
// 元素
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()
this.drawModels()
}
// 初始化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 drawModels() {
const img = document.createElement('img')
img.src = imgUrl;
//图片是异步加载,所以需要将图片加载完毕后,才进行渲染绘制
img.onload = () => {
this.canvas.drawImage(img, 0, 0, 50, 50);
}
}
}
修改src/style.scss
body {
background-color: #000;
//视图的宽度
width: 100vw;
//视图的高度
height: 100vh;
display: flex;
/*主轴*/
justify-content: center;
/*交叉轴*/
align-items: center;
//div画布默认就是居中
#app {
background-color: #333;
}
}
如下,效果就出来了:
将模型位置随机和大小可配置化。
修改src/config.ts
export default {
canvas: {
width: 900,
height: 600,
},
model:{
// 草地
straw:{
width: 30,
height: 30,
}
}
}
修改src/canvas/abstract/canvas.ts
import config from "../../config";
import imgUrl from '../../static/images/straw/straw.png'
/**
* 抽象类
*/
export default abstract class canvasAbstract {
// 元素
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()
this.drawModels()
}
// 初始化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 drawModels() {
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 position() {
return {
x: 20, y: 30
}
}
}
效果如下:
这里可以看出模型的渲染如果多次多个的话,代码特别冗余,可以抽写成工具函数。
新建src/service/image.ts
/**
* 服务
* 图片处理
*/
import config from "../config";
// keyof typeof config.images 提供非常好的类型提示
type mapKey = keyof typeof config.images
/**
* 贴图图片
*/
export const image = new Map<mapKey, HTMLImageElement>()
// Object.entries() 返回给定对象自身可枚举属性的键值对数组
// 例如
// { foo: 'bar', baz: 42 } => [ ['foo', 'bar'], ['baz', 42] ]
// 'foo' => [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]
export const promises = Object.entries(config.images).map(([key, value]) => {
// console.log(key,value)
return new Promise((resolve) => {
const img = document.createElement('img')
img.src = value
// //图片是异步加载,所以需要将图片加载完毕后,才进行渲染绘制
img.onload = () => {
image.set(key as mapKey, img)
resolve(img)
}
})
})
修改src/config.ts
import imgUrlStraw from './static/images/straw/straw.png'
import imgUrlTankTop from './static/images/tank/top.gif'
export default {
// 画布
canvas: {
width: 900,
height: 600,
},
// 模型
model: {
// 草地
straw: {
width: 30,
height: 30,
}
},
// 图片
images: {
// 草地
straw: imgUrlStraw,
tank: imgUrlTankTop
}
}
修改src/main.ts
import config from './config'
import './style.scss'
import './canvas/straw'
import {image, 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'))
}
void bootstrap()
优化模型贴图逻辑
修改src/main.ts
import config from './config'
import './style.scss'
import canvasStraw from './canvas/straw'
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()
}
void bootstrap()
增加抽象方法。
src/canvas/abstract/canvas.ts
import config from "../../config";
import {image} from "../../service/image";
/**
* 抽象类
*/
export default abstract class canvasAbstract {
// 元素
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:子类可以调用,外部不能调用
protected drawModels() {
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 position() {
return {
x: 20, y: 30
}
}
}
继承子类修复报错即可。
src/canvas/straw.ts
/**
* 画布
* 草地
*/
import canvasAbstract from "./abstract/canvas";
class straw extends canvasAbstract{
render(): void {
// super:调用父类的方法
super.drawModels()
}
}
// 草地在一个图层,所以只需要new一个实例即可。
export default new straw()
多个草地生成
为遵循开发规范,将src/canvas/abstract/canvas.ts修改为src/canvas/abstract/AbstractCanvas.ts
import config from "../../config";
import {image} from "../../service/image";
/**
* 抽象类
*/
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: 渲染多少个数量
protected drawModels(num: number) {
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 position() {
return {
x: Math.floor(
Math.random() *
config.canvas.width /
config.model.straw.width) *
config.model.straw.width,
y: Math.floor(
Math.random() *
config.canvas.height /
config.model.straw.height) *
config.model.straw.height,
}
}
}
修改src/config.ts
import imgUrlStraw from './static/images/straw/straw.png'
import imgUrlTankTop from './static/images/tank/top.gif'
export default {
// 画布
canvas: {
width: 900,
height: 600,
},
// 模型
model: {
// 草地
straw: {
width: 30,
height: 30,
}
},
straw: {
num: 20
},
// 图片
images: {
// 草地
straw: imgUrlStraw,
tank: imgUrlTankTop
}
}
修改src/canvas/straw.ts为src/canvas/Straw.ts
/**
* 画布
* 草地
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
class Straw extends AbstractCanvas {
render(): void {
// super:调用父类的方法
super.drawModels(config.straw.num)
}
}
// 草地在一个图层,所以只需要new一个实例即可。
export default new Straw()
这里随机20个草地:
解决草地重叠问题——批量生成唯一坐标
新增批量生成草地的方法
修改src/canvas/abstract/AbstractCanvas.ts
import config from "../../config";
import {image} from "../../service/image";
/**
* 抽象类
*/
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: 渲染多少个数量
protected drawModels(num: number) {
this.positionCollection(num).forEach((position) => {
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() {
return {
x: Math.floor(
Math.random() *
config.canvas.width /
config.model.straw.width) *
config.model.straw.width,
y: Math.floor(
Math.random() *
config.canvas.height /
config.model.straw.height) *
config.model.straw.height,
}
}
}
模型构建
以上只是在构造类中通过草地举例。下面将模型的渲染封装到model中。
将src/model/straw.ts改为src/model/ModelStraw.ts
/**
* 模型
* 草地
*/
import {image} from "../service/image";
import config from "../config";
export default class modelStraw {
constructor(
protected canvas: CanvasRenderingContext2D,
protected x: number,
protected y: number
) {
this.canvas.drawImage(
image.get("straw")!,
x,
y,
config.model.straw.width,
config.model.straw.height
)
}
}
修改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: any) {
this.positionCollection(num).forEach((position) => {
new model(this.canvas, position.x, position.y)
// 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() {
return {
x: Math.floor(
Math.random() *
config.canvas.width /
config.model.straw.width) *
config.model.straw.width,
y: Math.floor(
Math.random() *
config.canvas.height /
config.model.straw.height) *
config.model.straw.height,
}
}
}
修改src/canvas/Straw.ts
/**
* 画布
* 草地
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelStraw from '../model/ModelStraw'
class Straw extends AbstractCanvas {
render(): void {
// super:调用父类的方法
super.drawModels(config.straw.num, ModelStraw)
}
}
// 草地在一个图层,所以只需要new一个实例即可。
export default new Straw()
效果一致。
模型抽象类
同样,模型的构造也可以1抽象成公共构造类
新建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.canvas.drawImage(
image.get("straw")!,
x,
y,
config.model.straw.width,
config.model.straw.height
)
}
}
修改src/model/ModelStraw.ts
/**
* 模型
* 草地
*/
import AbstractModel from "./abstract/AbstractModel";
export default class modelStraw extends AbstractModel{
}
结果没有区别。
为草地等模型提供TS支持
可以将类型约束声明为全局。
修改src/vite-env.d.ts
/// <reference types="vite/client" />
/**
* 全局声明
*/
/**
* 模型对象
*/
interface ConstructorModel {
new(canvas: CanvasRenderingContext2D,
x: number,
y: number): any
}
/**
* 模型实现的函数、方法
*/
interface IModel {
}
修改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() {
this.canvas.drawImage(
image.get("straw")!,
this.x,
this.y,
config.model.straw.width,
config.model.straw.height
)
}
}
......
修改src/canvas/abstract/AbstractCanvas.ts
......
// 绘制模型
// 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()//渲染贴图
}
......
修改src/model/ModelStraw.ts,实现接口和父抽象类的抽象方法。
/**
* 模型
* 草地
*/
import AbstractModel from "./abstract/AbstractModel";
export default class modelStraw extends AbstractModel implements IModel {
// 继承父类抽象方法:渲染贴图
// 一些初始化自定义的动作、行为,都在这里进行
render(): void {
super.draw()
}
}
效果不变:
发表评论 (审核通过后显示评论):