【Tank】5.0 水域模型、钢墙模型、坦克模型
水域模型、钢墙模型
同砖墙模型的创建。
src/config.ts
// 草地
import imgUrlStraw from './static/images/straw/straw.png'
// 砖墙
import imgUrlWallBrick from './static/images/wall/wall.gif'
// 水域
import imgUrlWater from './static/images/water/water.gif'
// 钢墙
import imgUrlWallSteel from './static/images/wall/steels.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,
},
// 砖墙
wallBrick: {
num: 100,
},
// 钢墙
wallSteel:{
num: 50,
},
// 水域
water:{
num:40
},
// 图片
images: {
// 草地
straw: imgUrlStraw,
wallBrick: imgUrlWallBrick,
wallSteel:imgUrlWallSteel,
water:imgUrlWater,
tank: imgUrlTankTop
}
}
src/main.ts
import config from './config'
import './style.scss'
import canvasStraw from './canvas/Straw'
import canvasWallBrick from './canvas/WallBrick'
import canvasWater from './canvas/Water'
import canvasWallSteel from './canvas/WallSteel'
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()
canvasWallBrick.render()
canvasWater.render()
canvasWallSteel.render()
}
void bootstrap()
src/canvas/WallBrick.ts
/**
* 画布
* 墙
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelWall from '../model/WallBrick'
class WallBrick extends AbstractCanvas {
// 构造函数,初始时run一次
constructor() {
super();
// super:调用父类的方法
super.createModels(config.wallBrick.num, ModelWall)
}
render(): void {
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
}
// 墙在一个图层,所以只需要new一个实例即可。
export default new WallBrick()
src/canvas/WallSteel.ts
/**
* 画布
* 墙
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelWallSteel from '../model/WallSteel'
class WallSteel extends AbstractCanvas {
// 构造函数,初始时run一次
constructor() {
super();
// super:调用父类的方法
super.createModels(config.wallSteel.num, ModelWallSteel)
}
render(): void {
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
}
// 墙在一个图层,所以只需要new一个实例即可。
export default new WallSteel()
src/canvas/Water.ts
/**
* 画布
* 水域
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelWater from '../model/Water'
class Water extends AbstractCanvas {
// 构造函数,初始时run一次
constructor() {
super();
// super:调用父类的方法
super.createModels(config.water.num, ModelWater)
}
render(): void {
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
}
// 墙在一个图层,所以只需要new一个实例即可。
export default new Water()
src/model/WallBrick.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("wallBrick")!)
}
}
src/model/WallSteel.ts
/**
* 模型
* 草地
*/
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
export default class ModelWallSteel extends AbstractModel implements IModel {
// 继承父类抽象方法:渲染贴图
// 一些初始化自定义的动作、行为,都在这里进行
render(): void {
super.draw(image.get("wallSteel")!)
}
}
src/model/Water.ts
/**
* 模型
* 水域
*/
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
export default class ModelWater extends AbstractModel implements IModel {
// 继承父类抽象方法:渲染贴图
// 一些初始化自定义的动作、行为,都在这里进行
render(): void {
super.draw(image.get("water")!)
}
}
效果如下:
重构游戏元素生成逻辑
主要将模型数量和模型实例抽象出来,以便初始化。
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
// 抽象方法,返回模型
abstract model(): ConstructorModel
// 抽象方法:返回模型数量
abstract num(): number
// 初始化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() {
position.getPositionCollection(this.num()).forEach((position) => {
const model = this.model()
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 implements ICanvas{
render(): void {
// super:调用父类的方法
super.createModels()
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
// 抽象方法,返回模型
model(): ConstructorModel {
return ModelStraw;
}
// 抽象方法:返回模型数量
num(): number {
return config.straw.num;
}
}
// 草地在一个图层,所以只需要new一个实例即可。
export default new Straw()
src/canvas/WallBrick.ts
/**
* 画布
* 墙
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelWall from '../model/WallBrick'
class WallBrick extends AbstractCanvas implements ICanvas {
render(): void {
// super:调用父类的方法
super.createModels()
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
// 抽象方法,返回模型
model(): ConstructorModel {
return ModelWall;
}
// 抽象方法:返回模型数量
num(): number {
return config.wallBrick.num
}
}
// 墙在一个图层,所以只需要new一个实例即可。
export default new WallBrick()
src/canvas/WallSteel.ts
/**
* 画布
* 墙
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelWallSteel from '../model/WallSteel'
class WallSteel extends AbstractCanvas implements ICanvas {
render(): void {
// super:调用父类的方法
super.createModels()
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
// 抽象方法,返回模型
model(): ConstructorModel {
return ModelWallSteel;
}
// 抽象方法:返回模型数量
num(): number {
return config.straw.num
}
}
// 墙在一个图层,所以只需要new一个实例即可。
export default new WallSteel()
src/canvas/Water.ts
/**
* 画布
* 水域
*/
import config from "../config";
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelWater from '../model/Water'
class Water extends AbstractCanvas implements ICanvas{
// 抽象方法:渲染贴图
render(): void {
// super:调用父类的方法
super.createModels()
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
// 抽象方法,返回模型
model(): ConstructorModel {
return ModelWater;
}
// 抽象方法:返回模型数量
num(): number {
return config.water.num
}
}
// 墙在一个图层,所以只需要new一个实例即可。
export default new Water()
TS支持提醒优化,src/vite-env.d.ts
/// <reference types="vite/client" />
/**
* 全局声明
*/
/**
* 模型对象
*/
interface ConstructorModel {
new(canvas: CanvasRenderingContext2D,
x: number,
y: number): any
}
/**
* 模型实现的函数、方法
*/
interface IModel {
// 抽象方法:渲染贴图
render(): void
}
/**
* 画布实现的函数、方法
*/
interface ICanvas {
// 抽象方法:渲染贴图
render(): void
// 抽象方法,返回模型
model(): ConstructorModel
// 抽象方法:返回模型数量
num(): number
}
运行结果如下:
绘制敌方坦克
src/config.ts
// 草地
import imgUrlStraw from './static/images/straw/straw.png'
// 砖墙
import imgUrlWallBrick from './static/images/wall/wall.gif'
// 水域
import imgUrlWater from './static/images/water/water.gif'
// 钢墙
import imgUrlWallSteel from './static/images/wall/steels.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,
},
// 砖墙
wallBrick: {
num: 100,
},
// 钢墙
wallSteel:{
num: 40,
},
// 水域
water:{
num:40
},
// 水域
tank:{
num:40
},
// 图片
images: {
// 草地
straw: imgUrlStraw,
wallBrick: imgUrlWallBrick,
wallSteel:imgUrlWallSteel,
water:imgUrlWater,
tank: imgUrlTankTop
}
}
src/main.ts
import config from './config'
import './style.scss'
import canvasStraw from './canvas/Straw'
import canvasWallBrick from './canvas/WallBrick'
import canvasWater from './canvas/Water'
import canvasWallSteel from './canvas/WallSteel'
import canvasTank from './canvas/Tank'
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()
canvasWallBrick.render()
canvasWater.render()
canvasWallSteel.render()
canvasTank.render()
}
void bootstrap()
src/model/Tank.ts
/**
* 模型
* 草地
*/
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
export default class ModelTank extends AbstractModel implements IModel {
// 继承父类抽象方法:渲染贴图
// 一些初始化自定义的动作、行为,都在这里进行
render(): void {
super.draw(image.get("tank")!)
}
}
src/canvas/Tank.ts
/**
* 画布
* 坦克
*/
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelTank from "../model/Tank";
import config from "../config";
class Tank extends AbstractCanvas implements ICanvas {
render(): void {
// super:调用父类的方法
super.createModels()
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
// 抽象方法,返回模型
model(): ConstructorModel {
return ModelTank;
}
// 抽象方法:返回模型数量
num(): number {
return config.tank.num
}
}
// 坦克在一个图层,所以只需要new一个实例即可。
export default new Tank()
效果如下:
坦克渲染在最顶层,且方向随机
src/config.ts
// 草地
import imgUrlStraw from './static/images/straw/straw.png'
// 砖墙
import imgUrlWallBrick from './static/images/wall/wall.gif'
// 水域
import imgUrlWater from './static/images/water/water.gif'
// 钢墙
import imgUrlWallSteel from './static/images/wall/steels.gif'
// 坦克
import imgUrlTankTop from './static/images/tank/top.gif'
import imgUrlTankRight from './static/images/tank/right.gif'
import imgUrlTankLeft from './static/images/tank/left.gif'
import imgUrlTankBottom from './static/images/tank/bottom.gif'
export default {
// 画布
canvas: {
width: 900,
height: 600,
},
// 模型
model: {
// 草地
straw: {
width: 30,
height: 30,
}
},
//草地
straw: {
num: 100,
},
// 砖墙
wallBrick: {
num: 100,
},
// 钢墙
wallSteel:{
num: 30,
},
// 水域
water:{
num:40
},
// 水域
tank:{
num:40
},
// 图片
images: {
// 草地
straw: imgUrlStraw,
wallBrick: imgUrlWallBrick,
wallSteel:imgUrlWallSteel,
water:imgUrlWater,
tankTop: imgUrlTankTop,
tankRight: imgUrlTankRight,
tankBottom: imgUrlTankBottom,
tankLeft: imgUrlTankLeft,
}
}
src/canvas/Tank.ts
/**
* 画布
* 坦克
*/
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelTank from "../model/Tank";
import config from "../config";
import position from "../service/position";
class Tank extends AbstractCanvas implements ICanvas {
render(): void {
// super:调用父类的方法
this.createModels()
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
// 抽象方法,返回模型
model(): ConstructorModel {
return ModelTank;
}
// 抽象方法:返回模型数量
num(): number {
return config.tank.num
}
// 重写父类方法
// 绘制模型,生成模型实例,只负责创建实例
createModels() {
for (let i = 0; i < this.num(); i++) {
const pos = position.position()
const model = this.model()
//Y轴永远从0开始
const instance = new model(this.canvas, pos.x, 0)
this.models.push(instance)
}
}
}
// 坦克在一个图层,所以只需要new一个实例即可。
export default new Tank()
src/model/Tank.ts
/**
* 画布
* 坦克
*/
import AbstractCanvas from "./abstract/AbstractCanvas";
import ModelTank from "../model/Tank";
import config from "../config";
import position from "../service/position";
class Tank extends AbstractCanvas implements ICanvas {
render(): void {
// super:调用父类的方法
this.createModels()
// 调用渲染模型,防止每次重新渲染时,又生成新的模型实例
super.renderModels();
}
// 抽象方法,返回模型
model(): ConstructorModel {
return ModelTank;
}
// 抽象方法:返回模型数量
num(): number {
return config.tank.num
}
// 重写父类方法
// 绘制模型,生成模型实例,只负责创建实例
createModels() {
for (let i = 0; i < this.num(); i++) {
const pos = position.position()
const model = this.model()
//Y轴永远从0开始
const instance = new model(this.canvas, pos.x, 0)
this.models.push(instance)
}
}
}
// 坦克在一个图层,所以只需要new一个实例即可。
export default new Tank()
src/enum/enumPosition.ts
/**
* 枚举类
*
*/
// 方向
export enum EnumDirection {
top = 'top',
right = 'right',
bottom = 'bottom',
left = 'left'
}
Lodash——JS工具库
在src/model/Tank.ts中,如下代码可以优化如下:
......
// 随机取用其中一个图片
randomImage(): HTMLImageElement {
let img: HTMLImageElement;
switch (this.direction) {
case EnumDirection.top:
img = image.get('tankTop')!
break;
case EnumDirection.right:
img = image.get('tankRight')!
break;
case EnumDirection.bottom:
img = image.get('tankBottom')!
break;
case EnumDirection.left:
img = image.get('tankLeft')!
break;
default:
img = image.get('tankTop')!
break;
}
return img
}
......
安装lodash
npm install -D lodash
npm install -S @type/lodash
package.json
{
"name": "tank",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/lodash": "^4.14.182",
"sass": "^1.51.0",
"typescript": "^4.5.4",
"vite": "^2.9.7"
}
}
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 name: string
// 抽象方法:渲染贴图
abstract render(): void
// 渲染函数
protected draw(img: HTMLImageElement) {
this.canvas.drawImage(
img,
this.x,
this.y,
config.model.straw.width,
config.model.straw.height
)
}
}
src/model/Tank.ts
/**
* 模型
* 草地
*/
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
import {EnumDirection} from "../enum/enumPosition";
import {upperFirst} from 'lodash'
import config from "../config";
export default class ModelTank extends AbstractModel implements IModel {
name: string = 'tank';
// 方向
protected direction: EnumDirection = EnumDirection.top
// 继承父类抽象方法:渲染贴图
// 一些初始化自定义的动作、行为,都在这里进行
render(): void {
this.randomDirection();
super.draw(this.randomImage())
}
randomDirection() {
// 随机取一个
const index = Math.floor((Math.random() * 4))
this.direction = Object.keys(EnumDirection)[index] as EnumDirection
}
// 随机取用其中一个图片
randomImage(): HTMLImageElement {
return image.get(`${this.name}${upperFirst(this.direction)}` as keyof typeof config.images)!
// let img: HTMLImageElement;
// switch (this.direction) {
// case EnumDirection.top:
// img = image.get('tankTop')!
// break;
// case EnumDirection.right:
// img = image.get('tankRight')!
// break;
// case EnumDirection.bottom:
// img = image.get('tankBottom')!
// break;
// case EnumDirection.left:
// img = image.get('tankLeft')!
// break;
// default:
// img = image.get('tankTop')!
// break;
// }
// return img
}
}
src/model/Straw.ts、src/model/WallBrick.ts、src/model/WallSteel.ts、src/model/Water.ts修改类似,这里不重复贴代码。
/**
* 模型
* 草地
*/
import AbstractModel from "./abstract/AbstractModel";
import {image} from "../service/image";
import config from "../config";
export default class ModelStraw extends AbstractModel implements IModel {
name: string = 'straw';// 这里的名字换成对应的贴图名称即可
// 继承父类抽象方法:渲染贴图
// 一些初始化自定义的动作、行为,都在这里进行
render(): void {
super.draw(image.get(this.name as keyof typeof config.images)!)
}
}
效果不变:
发表评论 (审核通过后显示评论):