uniapp中vue2实现迷宫游戏

博主最近在做一款小游戏,不想用canvas去写,就想着用uniapp+vue2去实现,代码很简单,因为是uniapp,可以同时兼容H5和app端

H5端

下面是html的代码

<view class="game-content w-100 d-f f-d-c a-i-c">
				<view class="game-main">
					<!-- 迷宫图 -->
					<view class="game-main-map">
						<!-- 控制人物一定用绝对单位 px值 不要用rpx让uniapp去计算 -->
						<image v-if="lattice_height >= 0 || character_left >= 0" class="character"
							src="@/static/Snipaste_2024-12-16_17-18-31.png"
							:style="{ width: lattice_width + 'rpx', height: lattice_height + 'rpx', left: character_left + 'px', top: character_top + 'px' }">
						</image>
						<view class="map-bg w-100 d-f a-i-c j-c-s-b" v-for="(item, index) in map">
							<view class="map-samll-b d-f a-i-c"
								:class="[`map-samll-b-${index + 1}-${index1 + 1}`, item1 ==  0 ? 'map-samll-b-fideIn-animation' : '']"
								:style="{
									width: '100%',
									height: lattice_height + 'rpx',
									background: item1 == 1 ? 'rgb(245, 227, 183)' : '',
									animationDelay: item1 ==  0 ? `${index1 * 0.03}s` : '',
									borderRadius: computedRadius(index, index1, map, item)
								}" v-for="(item1, index1) in item">
								<image class="w-100 h-100" v-if="item1 == 1" src="@/static/gezi.png" mode=""></image>
							</view>
						</view>
					</view>
				</view>
				<!-- 控制器 -->
				<view class="controller w-100 h-100 d-f j-c-c m-t-30">
					<view class="controller-box w-100 h-100 d-f f-d-c j-c-c a-i-c">
						<!-- 上 UP -->
						<view class="up direction-pressed-animation" @click.stop="pressed('up')">
							<image class="direction-image" src="/static/up.png"></image>
						</view>
						<!-- 左(LEFT) 右(RIGHT) -->
						<view class="left_right d-f j-c-s-b">
							<view class="direction-pressed-animation" @click.stop="pressed('left')">
								<image class="direction-image" src="/static/left.png"></image>
							</view>
							<view class="direction-pressed-animation" @click.stop="pressed('right')">
								<image class="direction-image" src="/static/right.png"></image>
							</view>
						</view>
						<!-- 下(DOWN) -->
						<view class="down direction-pressed-animation" @click.stop="pressed('down')">
							<image class="direction-image" src="/static/down.png"></image>
						</view>
					</view>
				</view>
			</view>

css代码

// 通用颜色表
$_text_color_1: #93A3B7;
$_text_color_2: #FFFFFF;
$_text_color_3: #47D392;
$_card_bg1: #2C3948;
$_card_bg2: #222D3A;

$_footer_bg1: #2E3D4E;
$_partition_bg1: #1D2835;

$_font_size_32: 32rpx;
$_font_size_28: 28rpx;
$_font_size_26: 26rpx;
$_font_size_24: 24rpx;
$_font_size_20: 20rpx;


$_font_family_1: PingFangSC-Medium;
$_font_family_2: PingFangSC-Regular;

$_font_weight_500: 500;
$_font_weight_400: 400;






@font-face {
	font-family: '汉仪大隶书简';
	src: url('@/common/text-family/汉仪大隶书简.TTF');
}

@font-face {
	font-family: 'ALIBABA-PUHUITI-HEAVY';
	src: url('@/common/text-family/ALIBABA-PUHUITI-HEAVY.OTF');
}

@font-face {
	font-family: 'FZZCHJW';
	src: url('@/common/text-family/FZZCHJW.TTF');
}

*{
	box-sizing: border-box !important;
}



// 顶部标题样式(固定)
@mixin titleStyle {
	background-color: $_card_bg2;
	font-family: $_font_family_1;
	font-size: $_font_size_32;
	color: $_text_color_2;
	font-weight: $_font_weight_500;
}

// 卡片文字名称
@mixin titleName {
	font-family: $_font_family_2;
	font-size: $_font_size_20;
	color: $_text_color_1;
	font-weight: $_font_weight_400;
}	


.datetime-picker{
	width: 0%;
	background-color: $_card_bg1;
	position: absolute;
	top: 0px;
	left: -1000px;
	z-index: 999;
}

// ::v-deep .uni-date-editor{
// 	opacity: 0;
// }
::v-deep .uni-calendar-item--checked{
	background-color: $_text_color_3 !important;
}
::v-deep .uni-datetime-picker--btn{
	background-color: $_text_color_3 !important;
}



.uni-calendar__content{
	background-color: #242d39 !important;
}
.uni-calendar-item__weeks-box-text{
	color: #FFFFFF !important;
}
.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable{
	color: #464646 !important;
}
.uni-calendar-item__weeks-box .uni-calendar-item--checked{
	border-radius: 50% !important;
	border: none !important;
	background-color: #47D392 !important;
}
.uni-date-changed--time-date{
	color: #FFFFFF !important;
}
.uni-datetime-picker-text{
	color: #FFFFFF !important;
}
.uni-calendar__header-text{
	font-size: 35rpx !important;
	color: #FFFFFF !important;
}
.uni-calendar__header-btn{
	border-left-color: #47D392 !important;
	border-top-color: #47D392 !important;
	width: 22rpx !important;
	height: 22rpx !important;
	border-left-width: 4rpx !important;
	border-top-width: 4rpx !important;
}
.uni-calendar__weeks-day-text{
	color: #FFFFFF !important;
	font-size: 30rpx !important;
}
.uni-calendar__weeks-day{
	border: none !important;
}

.uni-datetime-picker-btn-text{
	font-size: 35rpx !important;
	color: #47D392 !important;
	// background-color: #242d39 !important;
}
.uni-date-changed{
	border: none !important;
}
.uni-calendar__box{
	border-bottom: 2rpx solid #435060 !important;
}

.uni-picker-action-confirm{
	color: #47D392 !important;
}

// .uni-picker-view-mask{
// 	background: linear-gradient(180deg, hsla(212, 24%, 23%, 0.95), hsla(212, 24%, 23%, .6)), linear-gradient(0deg, hsla(212, 24%, 23%, .95), hsla(212, 24%, 23%, .6));
// 	background-position: top, bottom;
// 	background-size: 100% 102px;
// 	background-repeat: no-repeat;
// }

.uni-datetime-picker-view{
	height: 220px !important;
	width: 350px !important;
}

.uni-calendar-item--isDay{
		width: 40px !important;
	    height: 40px !important;
	    border: 2px solid $_text_color_1 !important;
		left: 50% !important;
		top: 0px !important;
		right: 0px !important;
		transform: translateX(-50%);
		background-color: rgba(0,0,0,0) !important;
		position: absolute;
}

.zp-main-error-btn{
	border: none !important;
}

.loading{
	width: 100vw;
	height: 100vh;
	position: absolute;
	top: 0rpx;
	z-index: 1000 !important;
}

.u-loading-page__warpper{
	width: 200rpx;
	height: 200rpx;
	background-color: rgba(0,0,0,0.5);
	border-radius: 15rpx;
}

.uni-calendar__mask{
	z-index: 1002 !important;
}
.uni-calendar__content{
	z-index: 1003 !important;
}
.u-loading-page__warpper{
	margin-top: 0rpx  !important;
}

.statusBar{
	width: 100%;
	height: var(--status-bar-height);
}

.btn-active{
	transition: all .1s ease-in-out;
	
	&:active{
		opacity: 0.7;
	}
}

.btn-active1{
	transition: all .1s ease-in-out;
	
	&:active{
		opacity: 0.7;
		transform: scale(0.98);
	}
}



.partition {
	width: 100%;
	height: 20rpx;
	background-color: $_partition_bg1;
}




.w-100{
	width: 100% !important;
}

.h-100{
	height: 100% !important;
}

.o-y-s{
	overflow-y: scroll;
}

.o-y-s-h{}
.o-y-s-h::-webkit-scrollbar {
		display: none
}

.o-h{
	overflow: hidden;
}

.d-f{
	display: flex;
}

.f-d-c{
	flex-direction: column;
}

.a-i-c{
	align-items: center;
}
.a-i-s{
	align-items: flex-start !important;
}
.a-i-e{
	align-items: flex-end;
}

.j-c-c{
	justify-content: center;
}

.j-c-s-b{
	justify-content: space-between;
}

.j-c-s-a{
	justify-content: space-around !important;
}

.j-c-f-s{
	justify-content: flex-start;
}
.j-c-f-e{
	justify-content: flex-end;
}

.f-w-w{
	flex-wrap: wrap;
}

.f-1{
	flex: 1;
}

.p-10{
	padding: 10rpx;
}

.p-20{
	padding: 20rpx;
}
.p-30{
	padding: 30rpx;
}

.p-l-10{
	padding-left: 10rpx;
}
.p-l-20{
	padding-left: 20rpx;
}
.p-l-30{
	padding-left: 30rpx;
}

.p-r-10{
	padding-right: 10rpx;
}
.p-r-20{
	padding-right: 20rpx;
}
.p-r-30{
	padding-right: 30rpx;
}

.p-b-10{
	padding-bottom: 10rpx;
}
.p-b-20{
	padding-bottom: 20rpx;
}
.p-b-30{
	padding-bottom: 30rpx;
}


.p-t-10{
	padding-top: 10rpx;
}
.p-t-20{
	padding-top: 20rpx;
}
.p-t-30{
	padding-top: 30rpx;
}

.m-r-10{
	margin-right: 10rpx;
}
.m-r-20{
	margin-right: 20rpx;
}

.m-r-30{
	margin-right: 30rpx;
}

.m-l-10{
	margin-left: 10rpx;
}
.m-l-20{
	margin-left: 20rpx;
}

.m-l-30{
	margin-left: 30rpx;
}

.m-t-10{
	margin-top: 10rpx;
}
.m-t-20{
	margin-top: 20rpx;
}

.m-t-30{
	margin-top: 30rpx;
}

.m-b-10{
	margin-bottom: 10rpx;
}
.m-b-20{
	margin-bottom: 20rpx;
}
.m-b-30{
	margin-bottom: 30rpx;
}

.t-a-l{
	text-align: left;
}

.t-a-r{
	text-align: right;
}

.t-a-c{
	text-align: center;
}

	.game-content {
		.game-main {
			width: 680rpx;
			// padding: 15rpx;
			// background-image: linear-gradient(180deg, #bb0000 1%, #ce0002 100%);
			// border-radius: 28rpx;
			// border: 6rpx solid #fff9dc;
			overflow: hidden;

			.game-main-map {
				position: relative;

				.map-bg {}

				.map-samll-b {
					color: #000000;
					font-size: 15rpx;
					overflow: hidden;
				}

				.character {
					transition: all 0.1s ease-in;
					position: absolute;
					z-index: 1;
					transform: scale(1.5);
				}
			}
		}
	}

	.controller {
		.controller-box {
			width: 720rpx;
			height: 300rpx;

			.direction-image {
				width: 112rpx;
				height: 112rpx;
			}

			.left_right {
				width: 370rpx;
			}
		}
	}

js代码

首先是封装一个js专门管理地图

// 21 * 21地图

// 入口:21 - 2
// 出口:2 - 21

let ZJSX_MAP = {
	map1: { // 鼠
		intPut: '21-2',
		outPut: '2-21',
		array: [ // 鼠
			[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
			[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
			[1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1],
			[1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1],
			[1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1],
			[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
			[1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
			[1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1],
			[1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1],
			[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1],
			[1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
			[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
			[1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1],
			[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
			[1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
			[1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
			[1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1],
			[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1],
			[1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1],
			[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1],
			[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
		]
	},
}

export {
	ZJSX_MAP
}

下面是页面逻辑

<script>
	import {
		ZJSX_MAP
	} from '@/pages/mazeMap.js';
	import _ from 'loadsh';
	export default {
		data() {
			return {
				isStart: false, // 游戏是否开始
				map: [],
				character_left: 0, // 当前游戏人物的left值
				character_top: 0, // 当前游戏人物的top值
				lattice_width: 31, // 地图每一格的宽度 (包括游戏人物) 人物的宽高必须和格子的宽高一致
				lattice_height: 31, // 地图每一格的高度 (包括游戏人物) 人物的宽高必须和格子的宽高一致
				_step_x: 0, // 当前人物的x坐标也就是left值
				_step_y: 0 // 当前人物的y坐标也就是top值
			};
		},
		onLoad() {},
		mounted() {
			let str = 'map1';
			if (ZJSX_MAP[str]) {
				const {
					intPut,
					outPut,
					array
				} = ZJSX_MAP['map1'];
				this.setCharacterPosition(intPut, outPut);
				this.map = array;
			}
		},
		methods: {
			setCharacterPosition(intPut, outPut) {
				// 根据地图的入口初始化人物的位置
				this.$nextTick(() => {
					this._step_x = intPut.split('-')[0]; // 第几列
					this._step_y = intPut.split('-')[1]; // 第几行
					this.updateCharacterPosition();
				});
			},
			computedRadius(index, index1, map, item) {
				let radiusStr = '';
				if (index === 0) {
					radiusStr = index1 === 0 ? '5rpx 0rpx 0rpx 0rpx' : index1 === item.length - 1 ? '0rpx 5rpx 0rpx 0rpx' :
						'';
				} else if (index === map.length - 1) {
					radiusStr = index1 === 0 ? '0rpx 0rpx 0rpx 5rpx' : index1 === item.length - 1 ? '0rpx 0rpx 5rpx 0rpx' :
						'';
				}
				return radiusStr;
			},
			pressed: _.debounce(function(type = '') {
				// 得到当前人物的位置信息
				let _now_position = {
					_step_x: this._step_x,
					_step_y: this._step_y
				};
				// 按下方向健计算
				if (type == 'up') {
					// up = y 减去 1
					this.searchNextStep(_now_position, {
						x: 0,
						y: -1,
						type: 'up'
					});
				}
				if (type == 'left') {
					// left = x 减去 1
					this.searchNextStep(_now_position, {
						x: -1,
						y: 0,
						type: 'left'
					});
				}
				if (type == 'right') {
					// right = x 加上 1
					this.searchNextStep(_now_position, {
						x: +1,
						y: 0,
						type: 'right'
					});
				}
				if (type == 'down') {
					// up = y 加上 1
					this.searchNextStep(_now_position, {
						x: 0,
						y: +1,
						type: 'down'
					});
				}
			}, 200),
			searchNextStep({
				_step_x,
				_step_y
			}, {
				x,
				y,
				type
			}) {
				// 查找下一个盒子的信息
				console.log({
					x,
					y,
					type
				});

				let _next_position = {
					x: Number(_step_x) + x,
					y: Number(_step_y) + y
				};
				// _next_position.y 下一个格子是第几行
				// _next_position.x 下一个格子是第几列
				console.log(_next_position);
				// 得到下一个是否为墙壁或者不是墙壁
				let _next = this.map[_next_position.y - 1][_next_position.x - 1];
				if (_next == 1) {
					// 是墙壁 不允许通过
					return;
				}
				if (_next == 0) {
					// 可以通过
					this._step_x = _next_position.x; // 第几列
					this._step_y = _next_position.y; // 第几行
					// 更新方位
					this.updateCharacterPosition();
				}

				console.log('得到下一个盒子的方位', _next_position);
			},

			updateCharacterPosition() {
				// 更新人物的位置信息
				let _ele_name = `map-samll-b-${this._step_y}-${this._step_x}`;
				let _ele = document.getElementsByClassName(_ele_name)[0];
				let _ele_offset_x = _ele.offsetLeft;
				let _ele_offset_y = _ele.offsetTop;
				this.character_left = _ele_offset_x;
				this.character_top = _ele_offset_y;
			}
		}
	};
</script>

附上完整代码

<template>
	<!-- 拯救生肖 -->
	<z-layout :isShowBackView="true" :isShowBackIcon="false" zLayoutBg="/static/c.png" :isShowLogoIcon="false" sologo=""
		:sologoWidth="616" :sologoHeight="300">
		<template #z-layout-main>
			<view class="game-content w-100 d-f f-d-c a-i-c">
				<view class="game-main">
					<!-- 迷宫图 -->
					<view class="game-main-map">
						<!-- 控制人物一定用绝对单位 px值 不要用rpx让uniapp去计算 -->
						<image v-if="lattice_height >= 0 || character_left >= 0" class="character"
							src="@/static/Snipaste_2024-12-16_17-18-31.png"
							:style="{ width: lattice_width + 'rpx', height: lattice_height + 'rpx', left: character_left + 'px', top: character_top + 'px' }">
						</image>
						<view class="map-bg w-100 d-f a-i-c j-c-s-b" v-for="(item, index) in map">
							<view class="map-samll-b d-f a-i-c"
								:class="[`map-samll-b-${index + 1}-${index1 + 1}`, item1 ==  0 ? 'map-samll-b-fideIn-animation' : '']"
								:style="{
									width: '100%',
									height: lattice_height + 'rpx',
									background: item1 == 1 ? 'rgb(245, 227, 183)' : '',
									animationDelay: item1 ==  0 ? `${index1 * 0.03}s` : '',
									borderRadius: computedRadius(index, index1, map, item)
								}" v-for="(item1, index1) in item">
								<image class="w-100 h-100" v-if="item1 == 1" src="@/static/gezi.png" mode=""></image>
							</view>
						</view>
					</view>
				</view>
				<!-- 控制器 -->
				<view class="controller w-100 h-100 d-f j-c-c m-t-30">
					<view class="controller-box w-100 h-100 d-f f-d-c j-c-c a-i-c">
						<!-- 上 UP -->
						<view class="up direction-pressed-animation" @click.stop="pressed('up')">
							<image class="direction-image" src="/static/up.png"></image>
						</view>
						<!-- 左(LEFT) 右(RIGHT) -->
						<view class="left_right d-f j-c-s-b">
							<view class="direction-pressed-animation" @click.stop="pressed('left')">
								<image class="direction-image" src="/static/left.png"></image>
							</view>
							<view class="direction-pressed-animation" @click.stop="pressed('right')">
								<image class="direction-image" src="/static/right.png"></image>
							</view>
						</view>
						<!-- 下(DOWN) -->
						<view class="down direction-pressed-animation" @click.stop="pressed('down')">
							<image class="direction-image" src="/static/down.png"></image>
						</view>
					</view>
				</view>
			</view>
		</template>
	</z-layout>
</template>

<script>
	import {
		ZJSX_MAP
	} from '@/pages/mazeMap.js';
	import _ from 'loadsh';
	export default {
		data() {
			return {
				isStart: false, // 游戏是否开始
				map: [],
				character_left: 0, // 当前游戏人物的left值
				character_top: 0, // 当前游戏人物的top值
				lattice_width: 31, // 地图每一格的宽度 (包括游戏人物) 人物的宽高必须和格子的宽高一致
				lattice_height: 31, // 地图每一格的高度 (包括游戏人物) 人物的宽高必须和格子的宽高一致
				_step_x: 0, // 当前人物的x坐标也就是left值
				_step_y: 0 // 当前人物的y坐标也就是top值
			};
		},
		onLoad() {},
		mounted() {
			let str = 'map1';
			if (ZJSX_MAP[str]) {
				const {
					intPut,
					outPut,
					array
				} = ZJSX_MAP['map1'];
				this.setCharacterPosition(intPut, outPut);
				this.map = array;
			}
		},
		methods: {
			setCharacterPosition(intPut, outPut) {
				// 根据地图的入口初始化人物的位置
				this.$nextTick(() => {
					this._step_x = intPut.split('-')[0]; // 第几列
					this._step_y = intPut.split('-')[1]; // 第几行
					this.updateCharacterPosition();
				});
			},
			computedRadius(index, index1, map, item) {
				let radiusStr = '';
				if (index === 0) {
					radiusStr = index1 === 0 ? '5rpx 0rpx 0rpx 0rpx' : index1 === item.length - 1 ? '0rpx 5rpx 0rpx 0rpx' :
						'';
				} else if (index === map.length - 1) {
					radiusStr = index1 === 0 ? '0rpx 0rpx 0rpx 5rpx' : index1 === item.length - 1 ? '0rpx 0rpx 5rpx 0rpx' :
						'';
				}
				return radiusStr;
			},
			pressed: _.debounce(function(type = '') {
				// 得到当前人物的位置信息
				let _now_position = {
					_step_x: this._step_x,
					_step_y: this._step_y
				};
				// 按下方向健计算
				if (type == 'up') {
					// up = y 减去 1
					this.searchNextStep(_now_position, {
						x: 0,
						y: -1,
						type: 'up'
					});
				}
				if (type == 'left') {
					// left = x 减去 1
					this.searchNextStep(_now_position, {
						x: -1,
						y: 0,
						type: 'left'
					});
				}
				if (type == 'right') {
					// right = x 加上 1
					this.searchNextStep(_now_position, {
						x: +1,
						y: 0,
						type: 'right'
					});
				}
				if (type == 'down') {
					// up = y 加上 1
					this.searchNextStep(_now_position, {
						x: 0,
						y: +1,
						type: 'down'
					});
				}
			}, 200),
			searchNextStep({
				_step_x,
				_step_y
			}, {
				x,
				y,
				type
			}) {
				// 查找下一个盒子的信息
				console.log({
					x,
					y,
					type
				});

				let _next_position = {
					x: Number(_step_x) + x,
					y: Number(_step_y) + y
				};
				// _next_position.y 下一个格子是第几行
				// _next_position.x 下一个格子是第几列
				console.log(_next_position);
				// 得到下一个是否为墙壁或者不是墙壁
				let _next = this.map[_next_position.y - 1][_next_position.x - 1];
				if (_next == 1) {
					// 是墙壁 不允许通过
					return;
				}
				if (_next == 0) {
					// 可以通过
					this._step_x = _next_position.x; // 第几列
					this._step_y = _next_position.y; // 第几行
					// 更新方位
					this.updateCharacterPosition();
				}

				console.log('得到下一个盒子的方位', _next_position);
			},

			updateCharacterPosition() {
				// 更新人物的位置信息
				let _ele_name = `map-samll-b-${this._step_y}-${this._step_x}`;
				let _ele = document.getElementsByClassName(_ele_name)[0];
				let _ele_offset_x = _ele.offsetLeft;
				let _ele_offset_y = _ele.offsetTop;
				this.character_left = _ele_offset_x;
				this.character_top = _ele_offset_y;
			}
		}
	};
</script>

<style scoped lang="scss">
	.game-content {
		.game-main {
			width: 680rpx;
			// padding: 15rpx;
			// background-image: linear-gradient(180deg, #bb0000 1%, #ce0002 100%);
			// border-radius: 28rpx;
			// border: 6rpx solid #fff9dc;
			overflow: hidden;

			.game-main-map {
				position: relative;

				.map-bg {}

				.map-samll-b {
					color: #000000;
					font-size: 15rpx;
					overflow: hidden;
				}

				.character {
					transition: all 0.1s ease-in;
					position: absolute;
					z-index: 1;
					transform: scale(1.5);
				}
			}
		}
	}

	.controller {
		.controller-box {
			width: 720rpx;
			height: 300rpx;

			.direction-image {
				width: 112rpx;
				height: 112rpx;
			}

			.left_right {
				width: 370rpx;
			}
		}
	}
</style>

其中有个z-layout组件

附上代码

<template>
	<view class="z-layout o-y-s">
		<!-- 背景图 -->
		<image class="z-layout-bg w-100" :src="zLayoutBg"></image>
		<!-- 顶部占位 -->
		<view class="z-layout-empty-top w-100" v-if="isShowBackView"></view>
		
		<view class="z-layout-logo w-100 h-100">
			<view class="home-logo w-100 d-f j-c-c a-i-c" v-if="isShowLogoIcon">
				<image class="logo btn-active" src="@/static/home_logo_img_bg@3x.png"></image>
			</view>
		</view>
		<!-- 顶部导航栏 -->
		<view class="z-layout-nav w-100 d-f j-c-s-b a-i-c" v-if="isShowBackView">
			<view class="back-box h-100 d-f j-c-c a-i-c" v-if="isShowBackIcon" @click.stop="backTo">
				<image class="back btn-active" src="@/static/back.png" mode="aspectFit" ></image>
			</view>
			
		</view>
		<!-- 顶部导航栏占位 --> 
		<view class="z-layout-nav-empty-box w-100" v-if="isShowBackView"></view>
		
		<view class="home d-f f-d-c a-i-c w-100" v-if="isShowSologo">
			<view class="home-title-bg d-f j-c-c a-i-c w-100 ele-fideIn">
				<image :style="{ width: sologoWidth + 'rpx', height: sologoHeight + 'rpx' }" class="title-bg" :src="sologo"></image>
			</view>
			<!-- 图片占位 -->
			<view class="home-title-bg-empty w-100" :style="{ width: sologoWidth + 'rpx', height: sologoHeight + 'rpx' }"></view>
		</view>
		
		<!-- 内容部分 -->
		<slot name="z-layout-main"></slot>
		<slot></slot>
	</view>
</template>

<script>
	export default {
		name:"z-layout",
		props:{
			zLayoutBg:{
				type: String,
				default: '/static/home_img_bg@3x.png'
			},
			isShowBackView:{
				type: Boolean,
				default: true
			},
			isShowBackIcon:{
				type: Boolean,
				default: true
			},
			isShowSologo:{
				type: Boolean,
				default: true
			},
			isShowLogoIcon:{
				type: Boolean,
				default: true
			},
			sologo:{
				type: String,
				default: '/static/img_home_title@3x.png'
			},
			sologoWidth:{
				type: Number,
				default: 720
			},
			sologoHeight:{
				type: Number,
				default: 384
			},
		},
		data() {
			return {
				
			};
		},
		methods:{
			backTo(){
				uni.navigateBack({
					delta: 1,
					animationType: 'pop-out'
				})
			}
		}
	}
</script>

<style scoped lang="scss">

.z-layout{
	width: 100vw;
	height: 1624rpx;
	overflow: hidden;
	&-empty-top{
		height: 88rpx;
	}
	&-bg{
		position: absolute;
		z-index: -10;
		height: 1624rpx;
	}
	&-logo{
		position: absolute;
		top: 100rpx;
		z-index: -9;
		.home-logo{
			width: 200rpx;
			height: 56rpx;
			
			
			.logo{
				width: 200rpx;
				height: 63rpx;
			}
		}
	}
	
	&-nav{
		position: fixed;
		height: 88rpx;
		padding: 0rpx 28rpx;
		.back-box{
			position: absolute;
			z-index: 99;
			padding: 0rpx 10rpx;
			.back{
				width: 40rpx;
				height: 40rpx;
			}
			
		}
		
	}
	.z-layout-nav-empty-box{
		height: 88rpx;
	}
	
	.home{
		.home-title-bg{
			height: 384rpx;
			position: absolute;
			z-index: -9;
			.title-bg{
				width: 720rpx;
				height: 384rpx;
			}
		}
		.home-title-bg-empty{
			// height: 384rpx;
		}
	}
}
</style>

全局的scss动画文件

.btn-pressed-animation{
	&:active{
		animation: btn-pressed .1s forwards ease-in;
	}
}

@keyframes btn-pressed {
	0%{
		opacity: 0.9;
		transform: scale(1);
	}
	100%{
		opacity: 1;
		transform: scale(0.95);
	}
}

.ele-fideIn{
	animation: ele-fideIn .3s forwards ease-in-out;
}


@keyframes ele-fideIn {
	0%{
		opacity: 0;
	}
	100%{
		opacity: 1;
	}
}


.ele-fideOut{
	animation: ele-fideOut .3s forwards ease-in-out;
}

@keyframes ele-fideOut {
	0%{
		opacity: 1;
	}
	100%{
		opacity: 0;
	}
}



.sign-btn-fideIn-animation{
	animation: sign-btn-fideIn .3s forwards ease-in-out;
}

.sign-btn-fideOut-animation{
	animation: sign-btn-fideOut 1s forwards ease-in-out;
	animation-delay: .2s;
	transform-origin: center center;
}

// 立即签到按钮 出场动画
@keyframes sign-btn-fideIn {
	0%{
		opacity: 0.4;
		transform: translateY(10px);
	}
	100%{
		opacity: 1;
		transform: translateY(0px);
	}
}

// 立即签到按钮 退场动画
@keyframes sign-btn-fideOut {
	0%{
		opacity: 1;
		transform: translateY(0px);
	}
	70%{
	}
	100%{
		opacity: 0;
		transform: translateY(2500rpx) scale(0.1);
	}
}





// 抽奖盒子 动画
.Lottery-box-fideIn-animation{
	animation: Lottery-box-fideIn .5s forwards ease-in-out;
}

.Lottery-box-fideOut-animation{
	animation: Lottery-box-fideOut 0.6s forwards ease-in-out;
	animation-delay: .2s;
	transform-origin: center center;
}

// 抽奖盒子 出场动画
@keyframes Lottery-box-fideIn {
	0%{
		opacity: 0.4;
	}
	100%{
		opacity: 1;
	}
}

// 抽奖盒子 退场动画
@keyframes Lottery-box-fideOut {
	0%{
		transform: translateY(0px);
	}
	100%{
		transform: translateY(950rpx) scale(1.261);
	}
}





.Lottery-box-card-fideIn-animation{
	animation: Lottery-box-card-fideIn ease-in-out .3s forwards;
	animation-delay: 0.8s;
}


// 抽奖盒子 卡片出场动画
@keyframes Lottery-box-card-fideIn {
	0%{
		opacity: 0;
	}
	60%{
		opacity: 0.8;
	}
	100%{
		z-index: -8;
		opacity: 1;
		transform: translateY(-260rpx);
	}
}


.Lottery-box-card-text-fideIn-animation{
	animation: Lottery-box-card-text-fideIn ease-in-out .3s forwards;
	animation-delay: 1.1s;
}


// 抽奖盒子 卡片出场动画
@keyframes Lottery-box-card-text-fideIn {
	0%{
		opacity: 0;
	}
	60%{
		opacity: 0.8;
	}
	100%{
		z-index: -8;
		opacity: 1;
	}
}




// 活动首页
.activity-box-fideIn-animation{
	animation: activity-box-fideIn ease-in-out .3s forwards;
	animation-delay: 1.2s;
}


// 抽奖盒子 卡片出场动画
@keyframes activity-box-fideIn {
	0%{
		opacity: 0;
	}
	60%{
		opacity: 0.8;
	}
	100%{
		opacity: 1;
	}
}

// 活动首页
.activity-card-fideIn-animation1{
	animation: activity-card-fideIn ease-in-out .3s forwards;
	animation-delay: 1.3s;
	z-index: 1;
}
// 活动首页
.activity-card-fideIn-animation2{
	animation: activity-card-fideIn ease-in-out .3s forwards;
	animation-delay: 1.6s;
	z-index: 2;
}
// 活动首页
.activity-card-fideIn-animation3{
	animation: activity-card-fideIn ease-in-out .3s forwards;
	animation-delay: 1.8s;
	z-index: 3;
}
// 活动首页
.activity-card-fideIn-animation4{
	animation: activity-card-fideIn ease-in-out .3s forwards;
	animation-delay: 2s;
	z-index: 4;
}


// 抽奖盒子 卡片出场动画
@keyframes activity-card-fideIn {
	0%{
		opacity: 0.8;
	}
	100%{
		opacity: 1;
	}
}


// 方向键按下动画
.direction-pressed-animation{
	&:active{
		animation: direction-pressed ease-in .1s forwards;
	}
}


@keyframes direction-pressed {
	0%{
		transform: scale(1);
	}
	100%{
		transform: scale(0.8);
	}
}




// 迷宫路的动画
.map-samll-b-fideIn-animation{
	animation: map-samll-b-fideIn ease-in-out .5s forwards;
	// animation-delay: 2s;
}

@keyframes map-samll-b-fideIn {
	0%{
		opacity: 0.5;
		background-color: rgb(245, 227, 183);
		border-radius: 50%;
		transform: scale(1);
	}
	50%{
		transform: scale(2);
	}
	100%{
		background-color: rgba(107, 115, 0, 0.8);
		opacity: 1;
		transform: scale(1);
		border-radius: 0%;
	}
}


.xlsbz-card-bg2-box-fideIn{
	animation: xlsbz-card-bg2-box-fideIn ease-in-out .3s forwards;
}


// 降龙十八掌 游戏规则动画
@keyframes xlsbz-card-bg2-box-fideIn {
	0%{
		height: 0rpx;
	}
	
	100%{
		height: 1020rpx;
	}
}


.xlsbz-start-game-fideIn{
	animation: xlsbz-start-game-fideIn ease-in-out .3s forwards;
}


// 降龙十八掌 游戏规则动画
@keyframes xlsbz-start-game-fideIn {
	0%{
		height: 0rpx;
	}
	
	100%{
		height: 556rpx;
	}
}



@keyframes xlsbz-card-fideIn {
	0%{
		opacity: 0;
		z-index: -10;
		top: -1000rpx;
		left: -1000rpx;
		transform: scale(0.1, 0.1);
	}
	
	100%{
		opacity: 1;
		z-index: 0;
		top: 0rpx;
		left: 0rpx;
		transform: scale(1, 1);
	}
}


.submit-shadow-fideIn{
	animation: submit-shadow-fideIn ease .2s forwards;
	animation-delay: 0.5s;
}

@keyframes submit-shadow-fideIn {
	0%{
		width: 0rpx;
		transform: scale(1, 0.8);
	}
	
	100%{
		opacity: 1;
		width: 320rpx;
		transform: scale(1, 1);
	}
}



.xyqlz-character-box-animation{
	animation: xyqlz-character-box-fideIn ease .3s forwards;
	transform: left bottom;
}

@keyframes xyqlz-character-box-fideIn {
	0%{
		width: 0rpx;
		opacity: 0;
		transform: scale(1, 0.7);
	}
	
	100%{
		opacity: 1;
		width: 320rpx;
		left: 0rpx;
		transform: scale(1, 1);
	}
}

.xyqlz-character-animation{
	animation: xyqlz-character-fideIn ease 3.5s infinite;
	transform: left bottom;
}

@keyframes xyqlz-character-fideIn {
	0%{
		filter: drop-shadow(0px 0px 0rpx #fd392b);
	}
	50%{
		filter: drop-shadow(0px 0px 20rpx #09cdff);
	}
	100%{
		filter: drop-shadow(0px 0px 0rpx #fd7915);
	}
}


































### 使用 UniApp 开发小游戏 #### 项目配置与环境搭建 为了更好地理解和学习 UniApp,在创建一个小游戏之前,先要确保项目的配置正确无误。对于 `vite.config.ts` 文件,可以按照以下方式进行设置: ```typescript // vite.config.ts export default defineConfig({ server: { host: '0.0.0.0', // 解决控制台提示 Network: use --host to expose port: 8080, // 配置端口号 hmr: true, // 开启热更新 open: true // 启动时自动在浏览器中打开应用 } }) ``` 此部分配置有助于提高开发效率并简化调试过程[^3]。 #### 创建简单的非 Canvas 游戏逻辑 考虑到 canvas 的复杂性和特定用途,这里选择不使用 canvas 来构建一个简单的小游戏实例——比如经典的打砖块游戏或贪吃蛇游戏。这类游戏可以通过 HTML 和 CSS 实现基本界面,并利用 JavaScript 控制游戏逻辑和交互行为。 下面是一个简易版的“点击得分”小游戏示例代码片段: ```html <!-- index.html --> <template> <view class="game-container"> <text>Score: {{ score }}</text> <button @click="increaseScore">Click Me!</button> </view> </template> <script setup lang="ts"> import { ref } from 'vue' const score = ref(0) function increaseScore() { score.value++ } </script> <style scoped> .game-container { display: flex; justify-content: center; align-items: center; height: 100vh; } button { padding: 20px; font-size: large; } </style> ``` 这段代码展示了如何通过 Vue 组件的方式定义了一个具有计分功能的游戏页面。每当用户点击按钮时,分数就会增加一次。虽然这是一个非常基础的例子,但它体现了 UniApp 中组件化编程的思想以及数据绑定机制的应用[^1]。 #### 进一步优化与扩展 随着对框架熟悉度的增长,开发者可以根据需求引入更多高级特性来增强用户体验,如动画效果、音效支持或是更复杂的物理引擎集成等。同时也可以考虑将游戏移植到不同平台上去测试其跨平台能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值