【像素画板】游戏地图编辑器-uniapp项目开发流程详解

嘿,用过像素画板没有哦,相信喜欢绘画的小朋友会对它感兴趣呢,用来绘制像素画非常好看,有没有发现,它是可以用来绘制游戏地图的,是不是很好奇,来一起看看吧。

像素画板,也叫像素画的绘图工具,可绘制游戏素材,也是游戏地图编辑器

文章目录

  • 游戏地图
  • 创建项目
  • 初始页面
  • 画板页面
    • 初始化数据
    • 初始化画布
    • 触摸操作
    • 绘制逻辑
  • 运行项目
    • 像素画
    • 迷宫地图

游戏地图

有个案例,绘制的游戏地图在以下两个游戏中都有用到,可以看一看这两篇文章

  • 地下迷宫游戏-微信小程序开发流程详解

创建项目

这里用HBuilderX开发工具来创建一个uniapp项目,

例如项目名填写uniapp_map_edit,依次选择如下图
在这里插入图片描述

  • 选择新建uni-app项目
  • 使用默认模板
  • Vue版本选择 3

初始页面

这时项目是自动创建好的,找一找初始页面,

页面文件在项目里/pages/index/index.vue,打开文件修改布局,

要在<template>...</template>标签中修改布局

布局结果显示如下图
在这里插入图片描述

可以看到,页面使用了表单组件form

  • 一个滑块slider,设置画板像素宽度,也就是列数;
  • 一个多行输入框textarea,这里放置输出的地图数据,可直接修改,复制;
  • 三个按钮button,见名知意,什么用途不用多说吧

点击编辑地图数据按钮,就会跳转到画板页面了,

要在<script>...</script>标签中去写页面跳转javascript逻辑代码,很简单,自己能做出来,这里就不讲

画板页面

这个画板页面文件是没有的,需要自己创建一个,

创建页面文件在项目里/pages/game/game.vue

然后打开,同样是在<template>标签中修改,写好布局,显示页面如下图
在这里插入图片描述

从页面上看,布局中只用了一个canvas,和复选框,还有7个迷你按钮组件,

  • 复选框checkbox-groupcheckbox是控制是否显示绘制网格线的;
  • 7个迷你按钮button size="mini",从中选择一个像素来绘制,每个像素点表示不同的颜色;
  • 像素点可以表示数字,例如0,1,2,3,4...e,f
  • 游戏地图是用字节数字来表示的,占用内存少;

初始化数据

同样也是在<script>标签里去写逻辑代码

写好初始化逻辑,代码如下,

const app = getApp()
export default {data() {return {isShowGrids: true, //是否显示网格//所有按钮数据:文本,数值,颜色buttons: [{text: '0',value: '0',color: 'transparent'},//...{text: '6',value: '6',color: '#909399'},],currentKey: '1' //定义选择的按钮,如不同的画笔按钮};},/** 页面加载完毕会执行到这里 */onReady() {let {map, //地图数据cols //列数} = app.getMapData() //获取初始页面保存好的数据this.cols = cols//执行加载初始化方法this.load(map)},methods:{//...onTouchStart(e){...},onTouchMove(e){...},onTouchEnd(e){...},}
}

data()方法返回的是页面布局中使用到的数据,

做到这里,上面的画布页面就会显示好底部的一排按钮了,

如果显示效果不一样,就要调整布局对应样式,就在<style>...</style>标签中写CSS样式,

初始化画布

还有就是画布,现在还没有显示出来,继续写初始化画布代码,

methods里写加载的load(map)方法,代码如下

load(map) {const {cols} = thisuni.createSelectorQuery().select('#' + canvasId).fields({size: true}, res => {//定义画布数据this.canvasData = {canvas: {width: res.width,height: res.height},ctx: uni.createCanvasContext(canvasId)};//调用初始化画布方法this.initCanvas(map, cols || 24)}).exec()
},

同样的,调用的initCanvas(map, cols)方法也是在methods里面写,代码如下

initCanvas(map, cols) {const {ctx,canvas} = this.canvasData//计算出每个单元格大小const size = Math.floor(canvas.width / cols)//计算出铺满网格的行数const rows = Math.floor(canvas.height / size)//...const grids = []//...// 绘制网格,r是行数,c是列数for (let r = 0, i = 0; r < rows; r++) {for (let c = 0; c < cols; c++, i++) {let g = {x: c * size + paddingLeft, //位置x paddingLeft是左边距y: r * size + paddingTop,//...v: '0' //像素初始数据}//...grids.push(g)}}//将计算出的数据放到canvasData数据中,下次会读取到Object.assign(this.canvasData, {grids, //这是网格的数据size,//...rows})//调用重新绘制方法this.redraw()
},

触摸操作

画布显示出来以后,就要实现触摸绘制,

看之前的代码,有如下三个方法,分别是触摸开始,移动,结束事件调用的方法,就在这方法中实现

export default {//...methods:{//...onTouchStart(e){...},onTouchMove(e){...},onTouchEnd(e){...},}
}

在布局中的canvas组件需要加上属性关联绑定上面的三个方法

当用户触摸画布时,就在画布中画出一个点就可以了,代码如下

onTouchStart(e) {let touch1 = e.touches[0]//调用此方法,根据第一个触摸点查找网格中单元格的索引let index = this.findGridIndex(touch1);//如果没有在网格内,就返回if (index < 0) return;//...这是第二个触摸点,如果有的话,就实现触摸拖动像素点,来达到准确绘制let touch2 = e.touches[1]//...省略了//如果是点击,直接调用触摸移动方法即可,避免重复写this.onTouchMove(e)
},

调用触摸移动方法里实现了如何绘制像素点,代码如下

onTouchMove(e) {//如果是同时存在两个触摸点,就是拖动操作let isMove = e.touches.length > 1let touch = isMove ? e.touches[1] : e.touches[0];const {grids} = this.canvasData;const {currentKey} = this;if (isMove && this.selectGrids.grid) {//...处理拖动操作的}let index = this.findGridIndex(touch);if (index < 0) return;let grid = grids[index];//...//更新指定的像素信息Object.assign(grid, {v: currentKey,color: this.findCurrentColor(currentKey) //将数值转换为按钮对应的颜色方法});//调用重绘方法this.redraw();
},

这个触摸移动方法可以实现连续绘制像素点,就像画一条线,

在触摸结束的方法这里,如下代码,

onTouchEnd(e) {if (!e || e.touches.length > 0) return//...触摸多点触摸操作的
},

这个触摸结束方法是可有可无的,如果实现多点触摸就要去写

绘制逻辑

知道为什么叫重绘方法吗,它就用刷新逻辑来实现的,

绘制方法redraw(),代码如下,先擦干净,再画上去

redraw() {const {canvas,ctx,grids,size,rows,cols} = this.canvasDataconst {isShowGrids} = this//擦画板ctx.clearRect(0, 0, canvas.width, canvas.height)//设置画笔颜色ctx.strokeStyle = '#000000'ctx.fillStyle = '#ffffff'//画背景色,如果想背景透明,就注释掉这一行ctx.fillRect(0, 0, canvas.width, canvas.height)//定义单元格的一半大小let r = size / 2;for (let r = 0; r < rows; r++) {for (let c = 0; c < cols; c++) {let g = grids[r * cols + c]ctx.beginPath()if (g.v != '0') {//画颜色的ctx.fillStyle = g.colorctx.fillRect(g.x, g.y, size, size)} else if (isShowGrids) {//画网格的ctx.strokeRect(g.x, g.y, size, size)}}}//最后调用这个方法就绘制出来了ctx.draw(true)
},

看上面的方法是不是很简单,容易理解呢

运行项目

讲到这里,像素画板的小程序项目基本上就算做好了,可以编译运行,接下来看看效果

像素画

想当像素画板用,看看作者随便画的一个二哈,如下图,
在这里插入图片描述

这是像素画,笔者画得好看吗 (^o^);
对画画感兴趣的话,就自己想象画出来也好看的

如果调整的像素太细的话,或者网格列数过多,这是不好控制的,

这样,就要用两个手指触摸操作,项目里实现多点触摸操作逻辑是有点复杂的,

实现操作就是用一个手指按住画板,另一个手指去拖动,就会发现按住的像素点拖出来了,继续拖动到指定的位置即可,这就准确绘制了

迷宫地图

要想用来绘制游戏地图,例如迷宫地图,运行效果动图如下,
请添加图片描述

底部最后边的按钮是作者在项目里新加的预览图片功能,可以保存为图片的;
绘制好返回初始页面,可以看到显示导出的地图数据,

从上图中可以看到画出来导出的一串数字,点击复制地图数据按钮,然后粘贴到自己编写的小游戏程序中当新关卡地图用,

还可以绘制出2d像素人,像素地图,瓦片地图等,能想到的你都能用得上吧

例如,给地下迷宫游戏项目添加新的游戏地图,用法同如下代码

// 迷宫地图数据
const mapData1 = {map:' 111111111111111111111111101100010001000000000001000101101010101110101111111010101101010100000101000000010101101010111111101011111110101101010000000100010000000001101011101110101111111111101101000101000101000000000101101110101011101111111110101100010001010100010000000101111011111010111010111111101100010100010001010000000001101110101110101011101111111100000101000101000101000001111011101111101110101111101100010100000100000100000001101110111111111111101111101100000100000001000101000001111110101111101110111011111100000101000001010000010001101111101011111011111010101100000001010000010001000101111111111010101110101111101100000000010101000100000001101111101110101111101111101100000100000100000101000001111111111111111110111111111'cols: 27
}

导出的地图数据很长很长,粘贴时把后面多出的一串数字0去掉即可

想要项目源码在点这里查看下载,或者直接点这里搜索:像素画板,在本博客站内请放心下载,感谢支持!

可能手机上看不到,请改用电脑浏览器查看

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/193964.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言-归并排序

目录 1、归并排序基本思想 2、归并排序的实现&#xff08;递归法&#xff09; 2.1 代码实现递归法归并排序 3、归并排序的实现&#xff08;非递归法&#xff09; 3.1 修正边界问题 3.2 代码实现非递归法归并排序 结语&#xff1a; 前言&#xff1a; 归并排序是一种把数…

Python---格式化输出与%百分号----涉及转义符 \ 反斜杠的使用

相关链接Python--格式化输出中的转义符号----\t 制表符&#xff08;空格的&#xff09;和\n&#xff08;换行的&#xff09;_唯元素的博客-CSDN博客 Python---字符串&#xff08;用单、双引号、 三单/双引号定义。反斜杠 \ 转义&#xff0c;单在双内/双在单内 &#xff09;-CS…

力扣 --- 最后一个单词的长度

题目描述&#xff1a; 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例 1&#xff1a; 输入&#xff1a;s "Hello World&…

运维02:Linux

Linux安装 VMWare安装&#xff1a;夸克网盘分享&#xff08;提取码&#xff1a;refg&#xff09; CentOS安装&#xff1a;Index of /centos/7.9.2009/isos/x86_64/ Xshell安装&#xff1a;百度网盘 请输入提取码&#xff08;提取码&#xff1a;juau&#xff09; 环境准备 1、…

在Windows 11中,把iPhone照片和视频导出来又快又简单,无需第三方软件

如果你想将照片和视频从iPhone传输到Windows 11 PC&#xff0c;最快、最简单的方法是插入手机并执行自动导入。以下是操作方法。 如何将照片和视频从iPhone导入Windows 如果你用USB数据线将iPhone插入Windows PC&#xff0c;Windows 11可以像标准数码相机一样连接到它&#x…

react之封装有无Token(路由权限控制)的高阶组件

TOC 前景 有些路由页面内的内容信息比较敏感&#xff0c;如果用户没有经过登录获取到有效Token&#xff0c;是没有权限跳转的&#xff0c;根据Token的有 无控制当前路由是否可以跳转就是路由的权限控制 技术方案 实现步骤 1.在 components 目录中&#xff0c;创建 AuthRoute/in…

solidity实现ERC721代币标准发布NFT

文章目录 1、非同质化货币&#xff08;NFT&#xff09;- 维基百科2、IERC1653、IERC7214、IERC721Receiver5、IERC721Metadata6、ERC7217、ERC721 NFT 的实现8、编译部署 1、非同质化货币&#xff08;NFT&#xff09;- 维基百科 非同质化代币&#xff08;英语&#xff1a;Non-F…

Elasticsearch:什么是大语言模型(LLM)?

大语言模型定义 大语言模型 (LLM) 是一种深度学习算法&#xff0c;可以执行各种自然语言处理 (natural language processing - NLP) 任务。 大型语言模型使用 Transformer 模型&#xff0c;并使用大量数据集进行训练 —— 因此规模很大。 这使他们能够识别、翻译、预测或生成文…

时间复杂度为O (nlogn)的排序算法

归并排序 归并排序遵循分治的思想&#xff1a;将原问题分解为几个规模较小但类似于原问题的子问题&#xff0c;递归地求解这些子问题&#xff0c;然后合并这些子问题的解来建立原问题的解&#xff0c;归并排序的步骤如下&#xff1a; 划分&#xff1a;分解待排序的 n 个元素的…

【c】求一组数据的最大值和第二大的值

我们可以创建数组&#xff0c;利用冒泡排序法把数组进行排序&#xff0c;但是当元素过多时候循环可能过多导致循环超限 所以我们可以换种其他方法&#xff0c;代码附上 #include<stdio.h> int main() {int n,i;puts("输入这组数据的个数");scanf("%d&qu…

进行主从复制时出现的异常FATAL CONFIG FILE ERROR (Redis 6.2.6)Reading the configuration file

错误如下所示&#xff1a; FATAL CONFIG FILE ERROR (Redis 6.2.6) Reading the configuration file, at line 1 >>> include/myredis/redis.conf Bad directive or wrong number of arguments出现错误的原因是.conf文件中命令之间缺少空格&#xff0c;如下所示&…

QML中常见布局方法

目录 引言常见方法锚定&#xff08;anchors&#xff09;定位器Row、ColumnGridFlow 布局管理器RowLayout、ColumnLayoutGridLayoutStackLayout 总结 引言 UI界面由诸多元素构成&#xff0c;如Label、Button、Input等等&#xff0c;各种元素需要按照一定规律进行排布才能提高界…

Prime 2.0

信息收集 # Nmap 7.94 scan initiated Thu Nov 23 20:09:06 2023 as: nmap -sn -oN live.nmap 192.168.182.0/24 Nmap scan report for 192.168.182.1 Host is up (0.00018s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap scan report for 192.168.182.2 Host is u…

长度最小的子数组(Java详解)

目录 题目描述 题解 思路分析 暴力枚举代码 滑动窗口代码 题目描述 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条…

OpenCvSharp从入门到实践-(06)创建图像

目录 1、创建图像 1.1实例1-创建黑色图像 1.2实例2-创建白色图像 1.3实例3-创建随机像素的雪花点图像 2、图像拼接 2.1水平拼接图像 2.2垂直拼接图像 2.3实例4-垂直和水平两种方式拼接两张图像 在OpenCV中&#xff0c;黑白图像其实就是一个二维数组&#xff0c;彩色图像…

[ Linux Audio 篇 ] 音频开发入门基础知识

在短视频兴起的背景下&#xff0c;音视频开发越来越受到重视。接下来将为大家介绍音频开发者入门知识&#xff0c;帮助读者快速了解这个领域。 轻柔的音乐、程序员有节奏感的键盘声、嗡嗡的发动机、刺耳的手提钻……这些声音是如何产生的呢&#xff1f;又是如何传到我们耳中的…

SpringSecurity和JWT实现认证和授权

SpringSecurity和JWT实现认证和授权 框架介绍SpringSecurityJWT组成实例JWT实现认证和授权的原理 Hutool 使用表整合SpringSecurity及JWT在pom.xml中添加依赖添加JWT token的工具类添加RbacAdminService&#xff1a;添加自定义mapper创建SpringSecurity配置类添加ProjectSecuri…

Redis--14--BigKey 和 热点Key

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 BigKey1.什么是bigkey2.bigkey的危害3.发现bigkeyscan 4.解决bigkey 什么是热点Key&#xff1f;该如何解决1. 产生原因和危害原因危害 2.发现热点key预估发现客户端…

Stable Diffusion AI绘画系列【11】:超萌的Q版手办萌宠系列

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

送女朋友一个猜数字小游戏,猜对了会显示爱心(给你心爱的他或她一个惊喜)

起因是我在学习C语言完成老师布置C语言写一个猜数字的作业&#xff0c;突发奇想&#xff0c;能不能在这个猜对了之后弹出一个不一样的页面&#xff0c;然后就试试看能不能实现。基本思路是这样的&#xff1a; 1&#xff1a;先写一个C语言的猜数字的小游戏&#xff0c;在我上个文…