【Unity3D】实现2D小地图效果

目录

一、玩家脚本Player

二、Canvas组件设置

三、小地图相关

四、GameLogicMap脚本修改


  

基于:【Unity3D】Tilemap俯视角像素游戏案例-CSDN博客

2D玩家添加Dotween移动DOPath效果,移动完成后进行刷新小地图(小地图会顺便刷新大地图)

一、玩家脚本Player

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class Player : MonoBehaviour
{void Update(){if (Input.GetMouseButtonDown(0)){Vector2 pos = Input.mousePosition;Ray ray = Camera.main.ScreenPointToRay(pos);RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);if (hit.collider != null){Vector2 hitPos = hit.point;Vector3Int v3Int = new Vector3Int(Mathf.FloorToInt(hitPos.x), Mathf.FloorToInt(hitPos.y), 0);List<Vector3Int> pathPointList = GameLogicMap.Instance.PlayAstar(v3Int);Vector3[] pathArray = new Vector3[pathPointList.Count];int offset = pathPointList.Count - 1;for (int i = pathPointList.Count - 1; i >= 0; i--){                    Vector3Int pointPos = pathPointList[i];Vector3 worldPos = GameLogicMap.Instance.GetWorldPos(pointPos);pathArray[offset - i] = worldPos;}transform.DOPath(pathArray, 1f).OnComplete(() =>{Debug.Log("移动完成 更新小地图");GameLogicMap.Instance.DrawSmallMap();}).SetAutoKill(true);}}}public Vector3Int GetPos(){Vector3 pos = transform.position;return new Vector3Int(Mathf.FloorToInt(pos.x - 0.5f), Mathf.FloorToInt(pos.y - 0.5f), 0);}
}

二、Canvas组件设置

三、小地图相关

原理:利用2D游戏为了实现寻路而创建的二维数组去生成一张Texture2D纹理图,大地图是直接用int[,]map二维数组去创建,map[x,y]等于1是空地,不等于1是障碍物或不可穿越地形;
大地图上的玩家绿点是直接拿到玩家在map的坐标点直接绘制。

小地图是以玩家为中心的[-smallSize/2, smallSize/2]范围内进行绘制;小地图上的玩家绿点是直接绘制到中心点(smallSize.x/2, smallSize.y/2);

注意:绘制到Texture2D的像素点坐标是[0, size]范围的,则小地图的绘制是SetPixel(i, j, color),传递i, j是[0,size]范围的,而不要传递x, y,这个x,y的取值是以玩家点为中心的[-size/2, size/2]范围坐标值,如下取法:

x = 玩家点.x - size.x/2 + i;
y = 玩家点.y - size.y/2 + j;

用2层for遍历来看的话就是从(玩家点.x - size.x/2, 玩家点.y - size.y/2)坐标点,从下往上,从左往右依次遍历每个map[x,y]点生成对应颜色的像素点,构成一张Texture2D图片。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class SmallMapFor2D : MonoBehaviour
{public Image smallImage;public Vector2Int smallSize;private Vector2Int lastSmallSize;Texture2D smallImageTexture2D;public Image bigImage;Vector2Int bigSize;Texture2D bigImageTexture2D;private Vector3Int playerPoint;//环境二维数组地图[静态] 若是动态需改为自定义类数组,此时是int值类型数组private int[,] map;//初始化大地图整体public void InitBigMap(int[,] map, Vector2Int size){//已初始化则直接退出if (bigImageTexture2D != null)return;this.map = map;this.bigSize = size;bigImageTexture2D = new Texture2D(bigSize.x, bigSize.y);bigImageTexture2D.filterMode = FilterMode.Point;DrawBigMap();}public void DrawBigMap(){int mapWidth = map.GetLength(0);int mapHeight = map.GetLength(1);for (int i = 0; i < bigSize.x; i++){for (int j = 0; j < bigSize.y; j++){//判断是否在map范围内if (i < mapWidth && j < mapHeight){if (map[i, j] == 1) //空地{bigImageTexture2D.SetPixel(i, j, new Color(0.6f, 0.6f, 0.6f, 0.5f));}else //障碍物{bigImageTexture2D.SetPixel(i, j, new Color(0.3f, 0.3f, 0.3f, 0.5f));}}else{//非map范围内bigImageTexture2D.SetPixel(i, j, new Color(0.1f, 0.1f, 0.1f, 0.8f));}}}if (playerPoint != null){//更新大地图玩家点bigImageTexture2D.SetPixel(playerPoint.x, playerPoint.y, Color.green);}bigImageTexture2D.Apply();//bigImage.material.SetTexture("_MainTex", bigImageTexture2D);bigImage.material.mainTexture = bigImageTexture2D;bigImage.SetMaterialDirty();}//动态绘制小地图 时机:玩家移动完成后public void DrawSmallMap(Vector3Int playerPoint){//中途可换小地图大小if (this.lastSmallSize != null && this.lastSmallSize.x != smallSize.x && this.lastSmallSize.y != smallSize.y){if (smallImageTexture2D != null){Destroy(smallImageTexture2D);smallImageTexture2D = null;}smallImageTexture2D = new Texture2D(smallSize.x, smallSize.y);smallImageTexture2D.filterMode = FilterMode.Point;}this.lastSmallSize = smallSize;this.playerPoint = playerPoint;int mapWidth = map.GetLength(0);int mapHeight = map.GetLength(1);        for (int i = 0; i < smallSize.x; i++){for (int j = 0; j < smallSize.y; j++){//中心点是人物点 故绘制点为如下int x = playerPoint.x - smallSize.x / 2 + i;int y = playerPoint.y - smallSize.y / 2 + j;//判断是否在map范围内if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight){if (map[x, y] == 1) //空地{smallImageTexture2D.SetPixel(i, j, new Color(0.6f, 0.6f, 0.6f, 0.5f));}else //障碍物{smallImageTexture2D.SetPixel(i, j, new Color(0.3f, 0.3f, 0.3f, 0.5f));}}else{//非map范围内smallImageTexture2D.SetPixel(i, j, new Color(0.8f, 0f, 0f, 0.8f));}}}smallImageTexture2D.SetPixel(smallSize.x / 2, smallSize.y / 2, Color.green);        smallImageTexture2D.Apply();//smallImage.material.SetTexture("_MainTex", smallImageTexture2D);smallImage.material.mainTexture = smallImageTexture2D;smallImage.SetMaterialDirty();//更新大地图 因为玩家位置变化//(可优化 不需要重新整张地图再绘制 只更新上一个玩家点和新玩家点,只是要保存的大地图原始数据、旧玩家点更新即可)DrawBigMap();}
}

 小地图2张Image图片的材质球要使用不同的材质球实例

四、GameLogicMap脚本修改

主要变化新增和修改:

初始化绘制大地图和小地图
smallMapFor2D.InitBigMap(map, new Vector2Int(map.GetLength(0), map.GetLength(1)));
DrawSmallMap();

A*寻路方法返回路径数组,这是一个从终点到起点的数组,所以使用时是倒序遍历的。
public List<Vector3Int> PlayAstar(Vector3Int endPos)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Tilemaps;
using UnityEngine.UI;public class GameLogicMap : MonoBehaviour
{public static GameLogicMap _instance;public static GameLogicMap Instance{get { return _instance; }}public Grid terrainGrid;private Tilemap terrainTilemap;public Grid buildGrid;private Tilemap buildTilemap;public Player player;public int[,] map;private Vector3Int mapOffset;public SmallMapFor2D smallMapFor2D;private const int ConstZ = 0;public class Point{public Vector3Int pos;public Point parent;public float F { get { return G + H; } } //F = G + Hpublic float G; //G = parent.G + Distance(parent,self)public float H; //H = Distance(self, end)public string GetString(){return "pos:" + pos + ",F:" + F + ",G:" + G + ",H:" + H + "\n";}}private List<Point> openList = new List<Point>();private List<Point> closeList = new List<Point>();public LineRenderer lineRenderer;private void Awake(){_instance = this;}void Start(){terrainTilemap = terrainGrid.transform.Find("Tilemap").GetComponent<Tilemap>();buildTilemap = buildGrid.transform.Find("Tilemap").GetComponent<Tilemap>();BoundsInt terrainBound = terrainTilemap.cellBounds;BoundsInt buildBound = buildTilemap.cellBounds;map = new int[terrainBound.size.x, terrainBound.size.y];mapOffset = new Vector3Int(-terrainBound.xMin, -terrainBound.yMin, 0);Debug.Log("mapOffset:" + mapOffset);foreach (var pos in terrainBound.allPositionsWithin){var sprite = terrainTilemap.GetSprite(pos);if (sprite != null){SetMapValue(pos.x, pos.y, 1); //空地1}}foreach (var pos in buildBound.allPositionsWithin){var sprite = buildTilemap.GetSprite(pos);if (sprite != null){SetMapValue(pos.x, pos.y, 2); //障碍2}}smallMapFor2D.InitBigMap(map, new Vector2Int(map.GetLength(0), map.GetLength(1)));DrawSmallMap();}//绘制小地图public void DrawSmallMap(){//传递玩家在Map的坐标smallMapFor2D.DrawSmallMap(ToMapPos(player.GetPos()));}private void SetMapValue(int x, int y, int value){map[x + mapOffset.x, y + mapOffset.y] = value;}private Vector3Int ToMapPos(Vector3Int pos){return pos + mapOffset;}public List<Vector3Int> PlayAstar(Vector3Int endPos){endPos = ToMapPos(endPos);Debug.Log(endPos);openList.Clear();closeList.Clear();Vector3Int playerPos = player.GetPos();playerPos = ToMapPos(playerPos);openList.Add(new Point(){G = 0f,H = GetC(playerPos, endPos),parent = null,pos = playerPos,});List<Vector3Int> resultList = CalculateAstar(endPos);if (resultList != null){lineRenderer.positionCount = resultList.Count;for (int i = 0; i < resultList.Count; i++){Vector3Int pos = resultList[i];lineRenderer.SetPosition(i, GetWorldPos(pos));}}else{Debug.LogError("寻路失败;");}return resultList;}public Vector3 GetWorldPos(Vector3Int pos){pos.x = pos.x - mapOffset.x;pos.y = pos.y - mapOffset.y;return terrainTilemap.GetCellCenterWorld(pos);}private List<Vector3Int> CalculateAstar(Vector3Int endPos){int cnt = 0;while (true){//存在父节点说明已经结束            if (openList.Exists(x => x.pos.Equals(endPos))){Debug.Log("找到父节点~" + endPos + ",迭代次数:" + cnt);List<Vector3Int> resultList = new List<Vector3Int>();Point endPoint = openList.Find(x => x.pos.Equals(endPos));resultList.Add(endPoint.pos);Point parent = endPoint.parent;while (parent != null){resultList.Add(parent.pos);parent = parent.parent;}return resultList;}cnt++;if (cnt > 100 * map.GetLength(0) * map.GetLength(1)){Debug.LogError(cnt);return null;}//从列表取最小F值的Point开始遍历Point currentPoint = openList.OrderBy(x => x.F).FirstOrDefault();string str = "";foreach (var v in openList){str += v.GetString();}Debug.Log("最小F:" + currentPoint.GetString() + "\n" + str);Vector3Int pos = currentPoint.pos;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){if (i == 0 && j == 0){continue;}//过滤越界、墙体(map[x,y]不等于1)、已处理节点(存在闭合列表的节点)Vector3Int tempPos = new Vector3Int(i + pos.x, j + pos.y, ConstZ);if (tempPos.x < 0 || tempPos.x >= map.GetLength(0) || tempPos.y < 0 || tempPos.y >= map.GetLength(1)|| map[tempPos.x, tempPos.y] != 1|| closeList.Exists(x => x.pos.Equals(tempPos))){continue;}//判断tempPos该节点是否已经计算,  在openList的就是已经计算的Point tempPoint = openList.Find(x => x.pos.Equals(tempPos));float newG = currentPoint.G + Vector3.Distance(currentPoint.pos, tempPos);if (tempPoint != null){//H固定不变,因此判断旧的G值和当前计算出的G值,如果当前G值更小,需要改变节点数据的父节点和G值为当前的,否则保持原样float oldG = tempPoint.G;if (newG < oldG){tempPoint.G = newG;tempPoint.parent = currentPoint;Debug.Log("更新节点:" + tempPoint.pos + ", newG:" + newG + ", oldG:" + oldG + ",parent:" + tempPoint.parent.pos);}}else{tempPoint = new Point(){G = newG,H = GetC(tempPos, endPos),pos = tempPos,parent = currentPoint};Debug.Log("新加入节点:" + tempPoint.pos + ", newG:" + newG + ", parent:" + currentPoint.pos);openList.Add(tempPoint);}}}//已处理过的当前节点从开启列表移除,并放入关闭列表openList.Remove(currentPoint);closeList.Add(currentPoint);}}private float GetC(Vector3Int a, Vector3Int b){return Math.Abs(a.x - b.x) + Math.Abs(a.y - b.y);}
}

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

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

相关文章

四.3 Redis 五大数据类型/结构的详细说明/详细使用( hash 哈希表数据类型详解和使用)

四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09; 文章目录 四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09;2.hash 哈希表常用指令(详细讲解说明)2.1 hset …

C#通过3E帧SLMP/MC协议读写三菱FX5U/Q系列PLC数据案例

C#通过3E帧SLMP/MC协议读写三菱FX5U/Q系列PLC数据案例&#xff0c;仅做数据读写报文测试。附带自己整理的SLMP/MC通讯协议表。 SLMP以太网读写PLC数据20191206/.vs/WindowsFormsApp7/v15/.suo , 73216 SLMP以太网读写PLC数据20191206/SLMP与MC协议3E帧通讯协议表.xlsx , 10382…

【算法】经典博弈论问题——威佐夫博弈 python

目录 威佐夫博弈(Wythoff Game)【模板】 威佐夫博弈(Wythoff Game) 有两堆石子&#xff0c;数量任意&#xff0c;可以不同&#xff0c;游戏开始由两个人轮流取石子 游戏规定&#xff0c;每次有两种不同的取法 1)在任意的一堆中取走任意多的石子 2)可以在两堆中同时取走相同数量…

具身智能研究报告

参考&#xff1a; &#xff08;1&#xff09;GTC大会&Figure&#xff1a;“具身智能”奇点已至 &#xff08;2&#xff09;2024中国具身智能创投报告 &#xff08;3&#xff09;2024年具身智能产业发展研究报告 &#xff08;4&#xff09;具身智能行业深度&#xff1a;发展…

把本地搭建的hexo博客部署到自己的服务器上

配置远程服务器的git 安装git 安装依赖工具包 yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel安装编译工具 yum install -y gcc perl-ExtUtils-MakeMaker package下载git&#xff0c;也可以去官网下载了传到服务器上 wget https://www.ke…

STM32 旋转编码器

旋转编码器简介 旋转编码器&#xff1a;用来测量位置、速度或旋转方向的装置&#xff0c;当其旋转轴旋转时&#xff0c;其输出端可以输出与旋转速度和方向对应的方波信号&#xff0c;读取方波信号的频率和相位信息即可得知旋转轴的速度和方向 类型&#xff1a;机械触点式/霍尔传…

后盾人JS--闭包明明白白

延伸函数环境生命周期 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> <…

Python爬虫之——Cookie存储器

目录 专栏导读1、背景介绍2、库的安装3、核心代码4、完整代码总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——> 一晌小贪欢的博客主页求关注 &…

MFC开发,给对话框添加垂直滚动条并解决鼠标滚动响应的问题

无论在使用QT或者MFC进行界面开发时&#xff0c;都会出现在一个对话框里面存在好多的选项&#xff0c;导致对话框变得非常长或者非常大&#xff0c;就会显现的不美观&#xff0c;在这种情况下通常是添加一个页面的滚动条来解决这个问题&#xff0c;下面我们就来介绍给MFC的对话…

(undone) MIT6.S081 2023 学习笔记 (Day6: LAB5 COW Fork)

网页&#xff1a;https://pdos.csail.mit.edu/6.S081/2023/labs/cow.html 任务1&#xff1a;Implement copy-on-write fork(hard) (doing) 现实中的问题如下&#xff1a; xv6中的fork()系统调用会将父进程的用户空间内存全部复制到子进程中。如果父进程很大&#xff0c;复制过…

分享| RL-GPT 框架通过慢agent和快agent结合提高AI解决复杂任务的能力-Arxiv

结论 “RL-GPT: Integrating Reinforcement Learning and Code-as-policy” RL-GPT 框架为解决大语言模型在复杂任务处理中的难题提供了创新有效的途径&#xff0c; 旨在将强化学习&#xff08;RL&#xff09;和代码即策略相结合&#xff0c; 以解决大语言模型&#xff08…

【Linux权限】—— 于虚拟殿堂,轻拨密钥启华章

欢迎来到ZyyOvO的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创&#x1…

一个简单的自适应html5导航模板

一个简单的 HTML 导航模板示例&#xff0c;它包含基本的导航栏结构&#xff0c;同时使用了 CSS 进行样式美化&#xff0c;让导航栏看起来更美观。另外&#xff0c;还添加了一些 JavaScript 代码&#xff0c;用于在移动端实现导航菜单的展开和收起功能。 PHP <!DOCTYPE htm…

【算法应用】基于A*-蚁群算法求解无人机城市多任务点配送路径问题

目录 1.A星算法原理2.蚁群算法原理3.结果展示4.代码获取 1.A星算法原理 A*算法是一种基于图搜索的智能启发式算法&#xff0c;它具有高稳定性和高节点搜索效率。主要原理为&#xff1a;以起点作为初始节点&#xff0c;将其加入开放列表。从开放列表中选择具有最小总代价值 f (…

Python-基于PyQt5,json和playsound的通用闹钟

前言&#xff1a;刚刚结束2024年秋季学期的学习&#xff0c;接下来我们继续来学习PyQt5。由于之前我们已经学习了PyQt5以及PyUIC,Pyrcc和QtDesigner的安装&#xff0c;配置。所以接下来我们一起深入PyQt5&#xff0c;学习如何利用PyQt5进行实际开发-基于PyQt5&#xff0c;json和…

预测不规则离散运动的下一个结构

有一个点在19*19的平面上运动&#xff0c;运动轨迹为 一共移动了90步&#xff0c;顺序为 y x y x y x 0 17 16 30 10 8 60 15 15 1 3 6 31 10 7 61 14 15 2 12 17 32 9 9 62 16 15 3 4 12 33 10 9 63 18 15 4 3 18 34 15 12 6…

供应链系统设计-供应链中台系统设计(十)- 清结算中心概念片篇

综述 我们之前在供应链系统设计-中台系统设计系列&#xff08;五&#xff09;- 供应链中台实践概述文章中针对中台到底是什么进行了描述&#xff0c;对于中台的范围也进行划分&#xff0c;如下图所示&#xff1a; 关于商品中心&#xff0c;我们之前用4篇文章介绍了什么是商品中…

C27.【C++ Cont】时间、空间限制和STL库的简单了解

&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;春节篇&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8;&#x1f9e8; 目录 1.竞赛中的…

指针的介绍3后

1.函数指针变量 1.1函数的地址 void test(int (*arr)[2]) {printf("zl_dfq\n"); } int main() {printf("%p\n", test);printf("%p\n", &test);return 0; } 由上面的程序运行可知&#xff1a; 函数名就是函数的地址 &函数名也可以拿到函…

春晚舞台上的人形机器人:科技与文化的奇妙融合

文章目录 人形机器人Unitree H1的“硬核”实力传统文化与现代科技的创新融合网友热议与文化共鸣未来展望&#xff1a;科技与文化的更多可能结语 2025 年央视春晚的舞台&#xff0c;无疑是全球华人目光聚焦的焦点。就在这个盛大的舞台上&#xff0c;一场名为《秧BOT》的创意融合…