当前位置: 首页 > news >正文

cocos creator 3.x 手搓背包拖拽装备

 项目背景:

游戏背包 需要手动 拖拽游戏装备到 装备卡槽中,看了下网上资料很少。手搓了一个下午搞定,现在来记录下实现步骤;

功能拆分:

一个完整需求,我们一般会把它拆分成 几个小步骤分别造零件。等都造好了我们就能 装配到一起 形成一个完成功能。以下是对上面功能的 步骤拆分:

  1. 背包详情的展示,点击背包中的物品 展示一个详情页面(因为 是在 ScrollView 中拖拽 ScrollView 的元素 会造成 ScrollView 滚动)所有我们直接拖拽 详情页面中的元素就能 避免对 ScrollView 的影响。(如果没有详情可以 先生成一个一样的元素放置的 ScrollView 外层)
  2. 实现 拖拽元素,本身是对 拖拽事件的监听 来完成对元素位置的设置。这里有个知识点 触点坐标 到 世界坐标的转换
  3. 能拖拽元素了 那么我们就需要把 元素放在对应的位置, 实际上就是在拖拽的时候 判断两个元素的位置,根据需求我们这里是直接判断的 元素的 x坐标;来判断拖拽元素 是否到了 对应的卡槽;
  4. 既然能判断到那个具体卡槽了,我们停止拖拽,那就装备到对应的卡槽,要是没有到卡槽位置,我们隐藏拖拽元素 返回 (假装什么也没发生)

1.背包装备详情

点击装备展示,对应详情信息,应为装备位置不固定,

  1. 展示的时候就需要 通过 背包元素的 位置 来计算详情页面展示的位置
  2. 并且如果是 在边缘位置还要修改要展示的位置,然后修正位置信息后 在做展示

这里有一个技术点就是 坐标位置的转换

想要对比两个节点元素 或者 参照当前节点 设置另一个节点位置信息(不在同一个父节点的时候) 需要把他们转换在 同一个坐标系及(参照同一个父节点)

 

问题: 由于图中的 粉色ScrollView item  和 详情不是在用一个坐标参照中, 如果我们直接获取 item 位置 赋值给 详情元素 那肯定无法让他们两位置一直;

 

解决问题思路:

  先计 算出 item 世界坐标,然后再把这个坐标转化到 详情页面的局部坐标他们世界坐标一直,局部坐标不一致

同为 cocos creator 坐标转换需要使用 使用节点父元素计算:

https://docs.cocos.com/creator/3.8/api/zh/class/UITransform?id=convertToWorldSpaceARicon-default.png?t=N7T8http://convertToWorldSpaceAR

ItemworldPositon = item.parent.getComponent(UITransform).convertToWorldSpaceAR(item.getPosition());详情面板位置 =  详情面板.parent.getComponent(UITransform).convertToNodeSpaceAR(                  ItemworldPositon)
  • convertToNodeSpaceAR
    /*** @en* Converts a Point to node (local) space coordinates.** @zh* 将一个 UI 节点世界坐标系下点转换到另一个 UI 节点 (局部) 空间坐标系,这个坐标系以锚点为原点。* 非 UI 节点转换到 UI 节点(局部) 空间坐标系,请走 Camera 的 `convertToUINode`。** @param worldPoint @en Point in world space.*                   @zh 世界坐标点。* @param out @en Point in local space.*            @zh 转换后坐标。* @returns @en Return the relative position to the target node.*          @zh 返回与目标节点的相对位置。* @example* ```ts* const newVec3 = uiTransform.convertToNodeSpaceAR(cc.v3(100, 100, 0));* ```*/
    convertToNodeSpaceAR(worldPoint: math.Vec3, out?: math.Vec3): math.Vec3;
  • convertToWorldSpaceAR
    /*** @en* Converts a Point in node coordinates to world space coordinates.** @zh* 将距当前节点坐标系下的一个点转换到世界坐标系。** @param nodePoint @en Point in local space.*                  @zh 节点坐标。* @param out @en Point in world space.*            @zh 转换后坐标。* @returns @en Returns the coordinates in the UI world coordinate system.*          @zh 返回 UI 世界坐标系。* @example* ```ts* const newVec3 = uiTransform.convertToWorldSpaceAR(3(100, 100, 0));* ```*/
    convertToWorldSpaceAR(nodePoint: math.Vec3, out?: math.Vec3): math.Vec3;
 /*** 坐标计算* @param target ScrollView 中被点击的 item 元素 * @param item  数据*/
public setData(target: Node, item: CoachCharacterItem) {//console.log('target.getPosition():', target.getPosition());//当前节点本地坐标转世界坐标、//世界坐标转到 对应的 节点坐标let vec: Vec3 = this.rootParemt.getComponent(UITransform).convertToNodeSpaceAR(target.parent.getComponent(UITransform).convertToWorldSpaceAR(target.getPosition()));console.log(vec)//修正位置if (vec.x < 400) {vec.x += 250;} else {vec.x -= 250;}if (vec.y > 140) {vec.y = 140;} else if (vec.y < -100) {vec.y = -100;}this.node.setPosition(vec);this.node.active = true;
}

2.拖拽元素

我们给详情页面添加 拖拽事件的监听:

这里有个技术点 tuochMove 中获取到的坐标实际上是 触点坐标, 我们需要将这个坐标 转化到 UI坐标才能对 UI元素赋值

//添加监听事件
onLoad() {this.node.on(Node.EventType.TOUCH_START, this.touchStart, this);this.node.on(Node.EventType.TOUCH_MOVE, this.touchMove, this);this.node.on(Node.EventType.TOUCH_END, this.touchEnd, this)this.node.on(Node.EventType.TOUCH_CANCEL, this.touchCancel, this)
}//展示只有 在拖拽情况下才显示的 元素 equipmentFly
touchStart(event: EventTouch) {//Log.trace('touchStart');//展示 拖拽 元素 equipmentFlythis.equipmentFly.node.active = true;
}//获取 触点坐标位置
touchMove(event: EventTouch) {//更新 拖拽元素位置this.equipmentFly.updatePosition(event.getLocation());
}//结束拖拽 隐藏拖拽元素
touchCancel(event: EventTouch) {//console.log('touchCancel');//隐藏 拖拽 元素 equipmentFlythis.equipmentFly.node.active = false;//执行 拖拽完毕的判断逻辑this.equipmentFly.onConfirm();//重置拖拽元素的位置this.equipmentFly.resetPostion(new Vec3(0, 50));}
//结束拖拽
touchEnd(event: EventTouch) {//console.log('touchEnd');//隐藏 拖拽 元素 equipmentFlythis.equipmentFly.node.active = false;//执行 拖拽完毕的判断逻辑this.equipmentFly.onConfirm();//重置拖拽元素的位置this.equipmentFly.resetPostion(new Vec3(0, 50));
}
//隐藏详情面板
public hideNoticePanel() {this.equipmentFly.node.active = false;this.equipmentFly.resetPostion(new Vec3(0, 50));this.node.active = false;
}
触点坐标转换UI坐标的实现:

触点坐标转 UI 坐标需要通过  camera.screenToWorld(触点坐标)

Cocos Creator APIDescriptionicon-default.png?t=N7T8https://docs.cocos.com/creator/3.8/api/zh/class/renderer.scene.Camera?id=screenToWorld大概逻辑整理如下:

  1. 拿到 拖拽 事件传递的 触点坐标
  2. 触点坐标 通过 camera 转 世界坐标
  3. 世界坐标转 UI局部坐标赋值 拖拽元素坐标    这样就能实现 拖拽元素随着手指滑动而移动

//拿到摄像机
onLoad() {//获取摄像机this.camera = find('Canvas/Camera').getComponent(Camera);//获取卡槽1 的 世界坐标this.vec3WorldBattle = this.nodeWearBattle.parent.getComponent(UITransform).convertToWorldSpaceAR(this.nodeWearBattle.getPosition());//获取卡槽1 的 世界坐标this.vec3WorldReward = this.nodeWearReward.parent.getComponent(UITransform).convertToWorldSpaceAR(this.nodeWearReward.getPosition());}/*** 触点坐标转换 * 最后使用UI坐标赋值* @param vec */
updatePosition(vec: Vec2) {this.vec3Pos.x = vec.x;this.vec3Pos.y = vec.y;//触点坐标转 世界坐标this.vec3WorldNode = this.camera.screenToWorld(this.vec3Pos);// 世界坐标转 UI局部坐标this.vec3new = this.node.parent.getComponent(UITransform).convertToNodeSpaceAR(this.vec3WorldNode);this.node.position = this.vec3new;//计算 拖拽元素 和 两个卡槽的 x轴距离 这里  distance 为 50个单位if (Math.abs(this.vec3WorldNode.x - this.vec3WorldBattle.x) < this.distance) {//距离到了 通知界面展示 卡槽1 选中框this.coachCharacterEquipmentView.onPreview(this.equipment, 0);} else if (Math.abs(this.vec3WorldNode.x - this.vec3WorldReward.x) < this.distance) {       //距离到了 通知界面展示 卡槽2 选中框this.coachCharacterEquipmentView.onPreview(this.equipment, 1);} else {//这里处理 距离都没到的逻辑 移除选中框 以及 从 已经选中状态到 没选中状态的切换this.coachCharacterEquipmentView.onPreview(null, -1);}}

3.卡槽判定

通过上面的代码我们已经可以实现,拖转元素到 卡槽函数的调用:

//计算 拖拽元素 和 两个卡槽的 x轴距离 这里  distance 为 50个单位
if (Math.abs(this.vec3WorldNode.x - this.vec3WorldBattle.x) < this.distance) {//距离到了 通知界面展示 卡槽1 选中框this.coachCharacterEquipmentView.onPreview(this.equipment, 0);
} else if (Math.abs(this.vec3WorldNode.x - this.vec3WorldReward.x) < this.distance) {//距离到了 通知界面展示 卡槽2 选中框this.coachCharacterEquipmentView.onPreview(this.equipment, 1);
} else {//这里处理 距离都没到的逻辑 移除选中框 以及 从 已经选中状态到 没选中状态的切换this.coachCharacterEquipmentView.onPreview(null, -1);
}
因为我们一个卡槽有多个 装备位置,需要响应的装备展示 对应的卡槽;
this.coachCharacterEquipmentView.onPreview(data,soltIndex);

函数onPreview 当到对应的卡槽就把数据传递过去,具体不够就传 null,过去

卡槽现实的我们就略过去;

 

卡槽数据的确认:

这个环节就对应的是 到对应的卡槽,并且松手:

上面的代码中我们已经加过监听了;通过实践发现 即便我同时监听了 TOUCH_END  和 TOUCH_CANCEL 他也只有一个会执行;所有我两个都加了

所以一来逻辑就清晰了:

  1. 当拖拽元素到卡槽中的时候,我们已经传递参数过去了,并且 我们当拖出 卡槽的时候还把数据 赋值为  null,
  2. 当我们停止拖拽的时候 判断下 传递过去的 数据是否为 null,
  3. 如果为 null  则说明 没有在卡槽中停止 拖拽,不为空则为 在卡槽中停止的拖拽
touchCancel(event: EventTouch) {//console.log('touchCancel');this.equipmentFly.node.active = false;this.equipmentFly.onConfirm();this.equipmentFly.resetPostion(new Vec3(0, 50));}
touchEnd(event: EventTouch) {//console.log('touchEnd');this.equipmentFly.node.active = false;this.equipmentFly.onConfirm();this.equipmentFly.resetPostion(new Vec3(0, 50));
}

到这里基本上级功能都实现了。而且我们的 距离判断逻辑只有在 拖拽时候才会执行,应该说性能还不错!

相关文章:

cocos creator 3.x 手搓背包拖拽装备

项目背景&#xff1a; 游戏背包 需要手动 拖拽游戏装备到 装备卡槽中&#xff0c;看了下网上资料很少。手搓了一个下午搞定&#xff0c;现在来记录下实现步骤&#xff1b; 功能拆分&#xff1a; 一个完整需求&#xff0c;我们一般会把它拆分成 几个小步骤分别造零件。等都造好了…...

运维开发.Kubernetes探针与应用

运维系列 Kubernetes探针与应用 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263…...

Spring 框架:Java 企业级开发的基石

文章目录 序言Spring 框架的核心概念Spring 框架的主要模块Spring Boot&#xff1a;简化 Spring 开发Spring Cloud&#xff1a;构建微服务架构实际案例分析结论 序言 Spring 框架自 2002 年发布以来&#xff0c;已经成为 Java 企业级开发的标准之一。它通过提供全面的基础设施…...

在Docker中使用GPU

一、安装nvidia-container-toolkit 总之一句话&#xff1a;nvidia-docker和nvidia-docker2&#xff0c;nvidia-container-runtime 已经被英伟达迭代了&#xff0c;可以认为nvidia-container-toolkit是nvidia-docker和nvidia-docker2&#xff0c; nvidia-container-runtime 的替…...

vue3 前端实现导出下载pdf文件

这样的数据实现导出 yourArrayBufferOrByteArray 就是后端返回数据 // 创建Blob对象const blob new Blob([new Uint8Array(res)], { type: application/pdf })// 创建一个表示该Blob的URLconst url URL.createObjectURL(blob);// 创建一个a标签用于下载const a document.cr…...

AI智能体研发之路-模型篇(五):pytorch vs tensorflow框架DNN网络结构源码级对比

博客导读&#xff1a; 《AI—工程篇》 AI智能体研发之路-工程篇&#xff08;一&#xff09;&#xff1a;Docker助力AI智能体开发提效 AI智能体研发之路-工程篇&#xff08;二&#xff09;&#xff1a;Dify智能体开发平台一键部署 AI智能体研发之路-工程篇&#xff08;三&am…...

电商物流查询解决方案助力提升消费者体验

截至2023年12月&#xff0c;中国网络购物用户规模达9.15亿人&#xff0c;占网民整体的83.8%。这一庞大的数字不仅展现了电子商务的蓬勃发展&#xff0c;也标志着数字零售企业营销战略的转变——从以产品和流量为核心&#xff0c;到用户为王的新阶段。因此&#xff0c;提升消费者…...

【深度密码】神经网络算法在机器学习中的前沿探索

目录 &#x1f69d;前言 &#x1f68d;什么是机器学习 1. 基本概念 2. 类型 3. 关键算法 4. 应用领域 5. 工作流程 &#x1f68b;什么是神经网络 基本结构 &#x1f682;神经网络的工作原理 前向传播&#xff08;Forward Propagation&#xff09;&#xff1a; 损失函…...

搭载算能 BM1684 芯片,面向AI推理计算加速卡

搭载算能 BM1684 芯片&#xff0c;是面向AI推理的算力卡。可集成于服务器、工控机中&#xff0c;高效适配市场上所有AI算法&#xff0c;实现视频结构化、人脸识别、行为分析、状态监测等应用&#xff0c;为智慧城市、智慧交通、智慧能源、智慧金融、智慧电信、智慧工业等领域进…...

Python开发 我的世界 Painting-the-World: Minecraft 像素图片生成器

简介 Painting-the-World 是一款创新的工具&#xff0c;专为《我的世界》(Minecraft) 玩家及创作者设计&#xff0c;旨在将数字图片转变为游戏内的像素艺术。通过利用 RCON (Remote Console) 协议&#xff0c;本项目可以直接与《我的世界》服务器对话&#xff0c;根据输入的图…...

【经验分享】盘点“食用“的写文素材

一、构建框架 简介 1. 身份 擅长领域 2. 博客内容 3. 目前示例&#xff1a; 阿里云专家博主&#xff0c;华为云-云享专家&#xff0c;专注前、后端开发 博客内容&#xff1a;前后端实战教学、源码剖析、常见面试知识解析、算法题解与心得、日常考研总结等 目前正在备战考研&…...

实习碰到的问题w1

1.vueelementUI在输入框中按回车键会刷新页面 当一个 form 元素中只有一个输入框时&#xff0c;在该输入框中按下回车应提交该表单。如果希望阻止这一默认 行为&#xff0c;可以在 <el-form> 标签上添加 submit.native.prevent 。 参考&#xff1a;element-ui 表单 form …...

c#实现BPM系统网络传输接口,http协议,post

BPM通过http协议实现网络传输&#xff0c;语言使用.net(c#)&#xff0c;在这里只提供一个接口&#xff0c;具体代码如下,请参照&#xff1a; public string MakeRequest(string parameters) { ServicePointManager.ServerCertificateValidationCallback new Syst…...

如何修改开源项目中发现的bug?

如何修改开源项目中发现的bug&#xff1f; 目录 如何修改开源项目中发现的bug&#xff1f;第一步&#xff1a;找到开源项目并建立分支第二步&#xff1a;克隆分支到本地仓库第三步&#xff1a;在本地对项目进行修改第四步&#xff1a;依次使用命令行进行操作注意&#xff1a;Gi…...

结构设计模式 - 代理设计模式 - JAVA

代理设计模式 一. 介绍二. 代码示例2.1 定义 CommandExecutor 类2.2 定义 CommandExecutorProxy代理类2.3 模拟客户端2.4 测试结果 三. 结论 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子…...

企业了解这些cad图纸加密方法,再也不怕图纸被盗了!

在竞争激烈的商业环境中&#xff0c;企业的核心技术、设计图纸和创意是维持其市场地位和竞争优势的关键。CAD图纸作为产品设计的重要载体&#xff0c;其安全性自然成为企业关注的焦点。为了确保CAD图纸不被非法获取或盗用&#xff0c;企业需要采取一系列有效的加密方法。本文将…...

# 详解 JS 中的事件循环、宏/微任务、Primise对象、定时器函数,以及其在工作中的应用和注意事项

为什么会突然想到写这么一个大杂烩的博文呢&#xff0c;必须要从笔者几年前的一次面试说起 当时的我年轻气盛&#xff0c;在简历上放了自己的博客地址&#xff0c;而面试官应该是翻了我的博客&#xff0c;好几道面试题都是围绕着我的博文来提问 其中一个问题&#xff0c;直接…...

神经网络与深度学习——第14章 深度强化学习

本文讨论的内容参考自《神经网络与深度学习》https://nndl.github.io/ 第14章 深度强化学习 深度强化学习 强化学习&#xff08;Reinforcement Learning&#xff0c;RL&#xff09;&#xff0c;也叫增强学习&#xff0c;是指一类从与环境交互中不断学习的问题以及解决这类问题…...

centOS 编译C/C++

安装C和C编译器 yum -y install gcc*查看CenterOS系统信息 cat /etc/system-releaseCentOS Linux release 8.2.2004 (Core)查看gcc版本 gcc --versiongcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-4) Copyright (C) 2018 Free Software Foundation, Inc. This is free software…...

java——网络原理初识

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 目录 1.网络通信概念初识1.1 IP地址1.2端口号1.3协议1.3.1协议分层协议分层带来的好处主要有两个方面 1.3.2 TCP/IP五层 (或四层模型)1.3.3 协议的层和层之间是怎么配合工作的 1.网络通信概念初识…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...