【Unity造轮子】制作一个简单的2d抓勾效果(类似蜘蛛侠的技能)
前言
欢迎阅读本文,本文将向您介绍如何使用Unity游戏引擎来实现一个简单而有趣的2D抓勾效果,类似于蜘蛛侠的独特能力。抓勾效果是许多动作游戏和平台游戏中的常见元素,给玩家带来了无限的想象和挑战。




不需要担心,即使您是一位新手,也可以轻松跟随本文学习。我们将从头开始,从创建一个新的Unity项目开始,一直到最终的完成效果。
借助本文提供的步骤和技巧,您将能够为您的游戏增添一个的特色。希望您能享受这个过程,并从中获得灵感,探索更多关于游戏开发的乐趣。
照例,我们先来看看本文实现的最终效果,以决定你是否继续往下看


源码我放在文章末尾了
开始
1. 实现简单的抓勾效果
新建一个2d项目,添加一个2对象作为我们的角色物体,并挂载rigidbody 2d、碰撞器、Distance Joint 2d、Line Renderer(记得配置好材质和线宽)

书写脚本代码,代码已经加了详细的解释了,这里就不得过多介绍了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;// Grappler类,用于处理角色的抓取动作
public class Grappler : MonoBehaviour
{// 主摄像机public Camera mainCamera;// 线渲染器,用于渲染抓取线public LineRenderer _lineRenderer;// 距离关节,用于处理抓取物体的物理效果public DistanceJoint2D _distanceJoint;void Start(){// 初始化时,禁用距离关节_distanceJoint.enabled = false;}void Update(){// 检测鼠标左键是否按下if (Input.GetKeyDown(KeyCode.Mouse0)){// 获取鼠标在世界坐标中的位置Vector2 mousePos = (Vector2)mainCamera.ScreenToWorldPoint(Input.mousePosition);// 设置线渲染器的起始和结束位置_lineRenderer.SetPosition(0, mousePos);_lineRenderer.SetPosition(1, transform.position);// 设置距离关节的连接点_distanceJoint.connectedAnchor = mousePos;// 启用距离关节和线渲染器_distanceJoint.enabled = true;_lineRenderer.enabled = true;}// 检测鼠标左键是否松开else if (Input.GetKeyUp(KeyCode.Mouse0)){// 禁用距离关节和线渲染器_distanceJoint.enabled = false;_lineRenderer.enabled = false;}// 如果距离关节启用if (_distanceJoint.enabled){// 更新线渲染器的结束位置_lineRenderer.SetPosition(1, transform.position);}}
}
挂载脚本和绑定对象

简单配置一下环境

运行效果

可以看到,如果连线时碰撞会出现问题,如果你先实现好的碰撞效果,可以勾选Distance Joint 2d的Enable Collision及开启碰撞

效果

2. 高阶钩爪效果
在场景中创建GameObject如下(由父对象到子对象一一进行讲解):

Player:(示例中是一个 圆形的sprite)对其添加RigidBody2D、Circle Collider2D 、Spring Joint2D组件(跟前面一样Spring Joint2D组件开启Enable Collision碰撞)
Gunpivot:钩锁枪的锚点,其为空对象,位置设置在Player的中心即0.0位置(后续用于实现钩锁枪随着鼠标旋转的效果)
GrapplingGun:(示例中为一个长方形的sprite)钩锁枪,用于后期实现发射钩锁,仅添加Box Collider2D即可
FirePoint:钩锁的发射点,空对象,即钩锁发射的起始位置,设置在钩锁枪的边缘即可
Rope:在空对象上添加LineRenderer并适当改变宽度和材质即可。
下有两个脚本分别添加给GrapplingGun和Rope即可
脚本1:Perfecter_Grapple添加给GrapplingGun
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Perfecter_Grapple : MonoBehaviour
{[Header("脚本引用:")]public Grappling_Rope grappleRope;[Header("层设置:")][SerializeField] private bool grappleToAll = false;[SerializeField] private int grappableLayerNumber = 9;[Header("主摄像机:")]public Camera m_camera;[Header("变换引用:")]public Transform gunHolder;public Transform gunPivot;public Transform firePoint;[Header("物理引用:")]public SpringJoint2D m_springJoint2D;public Rigidbody2D m_rigidbody;[Header("旋转:")][SerializeField] private bool rotateOverTime = true;[Range(0, 60)] [SerializeField] private float rotationSpeed = 4;[Header("距离:")][SerializeField] private bool hasMaxDistance = false;[SerializeField] private float maxDistnace = 20;private enum LaunchType //发射类型{Transform_Launch,Physics_Launch}[Header("发射:")][SerializeField] private bool launchToPoint = true;[SerializeField] private LaunchType launchType = LaunchType.Physics_Launch;[SerializeField] private float launchSpeed = 1;[Header("无发射点")][SerializeField] private bool autoConfigureDistance = false;[SerializeField] private float targetDistance = 3;[SerializeField] private float targetFrequncy = 1;[HideInInspector] public Vector2 grapplePoint;[HideInInspector] public Vector2 grappleDistanceVector;private void Start() //开始{grappleRope.enabled = false;m_springJoint2D.enabled = false;}private void Update() //更新函数,控制输入{if (Input.GetKeyDown(KeyCode.Mouse0)) //通过Input的顺序设定函数的执行顺序,先进行钩爪选取点的定位{SetGrapplePoint(); }else if (Input.GetKey(KeyCode.Mouse0)) //从上一步的定位中把grapplerope启用{if (grappleRope.enabled){RotateGun(grapplePoint, false); //进行钩锁枪的旋转}else{Vector2 mousePos = m_camera.ScreenToWorldPoint(Input.mousePosition);RotateGun(mousePos, true);}if (launchToPoint && grappleRope.isGrappling) //如果选择点对点发射且正在钩中目标{if (launchType == LaunchType.Transform_Launch) //如果发射类型是使用Transform类型发射{Vector2 firePointDistnace = firePoint.position - gunHolder.localPosition;Vector2 targetPos = grapplePoint - firePointDistnace;gunHolder.position = Vector2.Lerp(gunHolder.position, targetPos, Time.deltaTime * launchSpeed); //采用插值的形式,模拟绳索命中的物理效果}}}else if (Input.GetKeyUp(KeyCode.Mouse0)) //若抬起左键,则将一切启用的相关布尔置否,恢复原状{grappleRope.enabled = false;m_springJoint2D.enabled = false;m_rigidbody.gravityScale = 1;}else //时刻获取鼠标的屏幕信息位置{Vector2 mousePos = m_camera.ScreenToWorldPoint(Input.mousePosition);RotateGun(mousePos, true);}}void RotateGun(Vector3 lookPoint, bool allowRotationOverTime) //实现绳索枪根据鼠标进行旋转功能{Vector3 distanceVector = lookPoint - gunPivot.position; //定义三维距离向量=朝向点-枪锚点位置float angle = Mathf.Atan2(distanceVector.y, distanceVector.x) * Mathf.Rad2Deg; //定义一个角度,其值等于距离向量tan所对应的弧度值*弧度值转化为角度值的常量if (rotateOverTime && allowRotationOverTime) //当采用根据时间延迟旋转时,采用四元数的插值旋转,在原本的旋转角和获得的绕轴的新角度中进行随时间{gunPivot.rotation = Quaternion.Lerp(gunPivot.rotation, Quaternion.AngleAxis(angle, Vector3.forward), Time.deltaTime * rotationSpeed);}else{gunPivot.rotation = Quaternion.AngleAxis(angle, Vector3.forward); //不采用时间插值变化时时,直接让强旋转角角度等于计算出的角度绕轴的四元数即可}}void SetGrapplePoint() //设定钩取点(主要是位置的计算和注意某些添加的限定条件){Vector2 distanceVector = m_camera.ScreenToWorldPoint(Input.mousePosition) - gunPivot.position; //设置一个二维向量distance用于记录鼠标点击的点和枪锚点之间的距离if (Physics2D.Raycast(firePoint.position, distanceVector.normalized)) //发射一条射线,起始点为开火点,方向为distance的方向向量{RaycastHit2D _hit = Physics2D.Raycast(firePoint.position, distanceVector.normalized); //保存刚才的射线为hitif (_hit.transform.gameObject.layer == grappableLayerNumber || grappleToAll) //选择是否选中任意的可抓取图层或是某一指定图层{if (Vector2.Distance(_hit.point, firePoint.position) <= maxDistnace || !hasMaxDistance) //当命中点和开火电站之间的距离小于最大距离或者不限定最大距离时{grapplePoint = _hit.point; //将命中点位置赋予抓取点位置grappleDistanceVector = grapplePoint - (Vector2)gunPivot.position; //抓钩的距离向量等于钩锁点减去钩锁枪的锚点位置grappleRope.enabled = true; //打开绳索变量}}}}public void Grapple() //钩锁执行(真正决定移动) {m_springJoint2D.autoConfigureDistance = false; //设定弹簧关节组建的自动计算距离属性为假if (!launchToPoint && !autoConfigureDistance) //当对点发射和自动计算距离均为假时,将目标距离和目标频率赋给弹簧组件的属性{m_springJoint2D.distance = targetDistance;m_springJoint2D.frequency = targetFrequncy;}if (!launchToPoint) //如果仅为不对点发射{if (autoConfigureDistance) //若自动计算距离{m_springJoint2D.autoConfigureDistance = true;m_springJoint2D.frequency = 0; //弹簧组件频率属性为0,该值越大,弹簧越硬}m_springJoint2D.connectedAnchor = grapplePoint; //不自动计算距离且不对点发射时m_springJoint2D.enabled = true;}else //对点发射时,选择发射类型,有物理类发射和Transform类发射{switch (launchType){case LaunchType.Physics_Launch:m_springJoint2D.connectedAnchor = grapplePoint; //当使用物理发射时,将钩取点赋予弹簧的连接锚点Vector2 distanceVector = firePoint.position - gunHolder.position; //长度变量等于开火点距离减去持枪距离m_springJoint2D.distance = distanceVector.magnitude; //将长度变量赋给弹簧组建的距离属性,保证钩爪拉到尽头时有一定的距离m_springJoint2D.frequency = launchSpeed; //弹簧频率(强度)等于发射速度m_springJoint2D.enabled = true; //打开弹簧组件,进行拉伸break;case LaunchType.Transform_Launch:m_rigidbody.gravityScale = 0; //当使用Transform发射时,将物体的重力设置为0m_rigidbody.velocity = Vector2.zero; //启动钩爪时,将物体速度清零break;}}}private void OnDrawGizmosSelected() //始终在场景中绘制可视的Gizmo,On方法{if (firePoint != null && hasMaxDistance){Gizmos.color = Color.green;Gizmos.DrawWireSphere(firePoint.position, maxDistnace);}}}
脚本2:Grappling_Rope 添加给Rope即可
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Grappling_Rope : MonoBehaviour
{[Header("一般引用:")]public Perfecter_Grapple grapplingGun; //抓钩枪public LineRenderer m_lineRenderer; //线渲染器[Header("一般设置:")][SerializeField] private int percision = 40; //精度[Range(0, 20)] [SerializeField] private float straightenLineSpeed = 5; //直线速度[Header("绳索动画设置:")]public AnimationCurve ropeAnimationCurve; //绳索动画曲线[Range(0.01f, 4)] [SerializeField] private float StartWaveSize = 2; //起始波动大小float waveSize = 0; //波动大小[Header("绳索进度:")]public AnimationCurve ropeProgressionCurve; //绳索进度曲线[SerializeField] [Range(1, 50)] private float ropeProgressionSpeed = 1; //绳索进度速度float moveTime = 0; //移动时间[HideInInspector] public bool isGrappling = true; //是否正在抓取bool strightLine = true; //是否为直线private void OnEnable() //启用时执行{moveTime = 0;m_lineRenderer.positionCount = percision;waveSize = StartWaveSize;strightLine = false;LinePointsToFirePoint(); //线点对准发射点m_lineRenderer.enabled = true;}private void OnDisable() //禁用时执行{m_lineRenderer.enabled = false;isGrappling = false;}private void LinePointsToFirePoint() //线点对准发射点{for (int i = 0; i < percision; i++){m_lineRenderer.SetPosition(i, grapplingGun.firePoint.position); //绘制连接抓取点和抓钩枪位置的绳子}}private void Update() //更新函数{moveTime += Time.deltaTime;DrawRope(); //绘制绳索}void DrawRope() //绘制绳索{if (!strightLine){if (m_lineRenderer.GetPosition(percision - 1).x == grapplingGun.grapplePoint.x){strightLine = true;}else{DrawRopeWaves(); //绘制绳索波动}}else{if (!isGrappling){grapplingGun.Grapple(); //抓取isGrappling = true;}if (waveSize > 0){waveSize -= Time.deltaTime * straightenLineSpeed;DrawRopeWaves(); //绘制绳索波动}else{waveSize = 0;if (m_lineRenderer.positionCount != 2) { m_lineRenderer.positionCount = 2; }DrawRopeNoWaves(); //绘制无波动的绳索}}}void DrawRopeWaves() //绘制绳索波动{for (int i = 0; i < percision; i++){float delta = (float)i / ((float)percision - 1f);Vector2 offset = Vector2.Perpendicular(grapplingGun.grappleDistanceVector).normalized * ropeAnimationCurve.Evaluate(delta) * waveSize; //计算偏移量Vector2 targetPosition = Vector2.Lerp(grapplingGun.firePoint.position, grapplingGun.grapplePoint, delta) + offset; //目标位置Vector2 currentPosition = Vector2.Lerp(grapplingGun.firePoint.position, targetPosition, ropeProgressionCurve.Evaluate(moveTime) * ropeProgressionSpeed); //当前位置m_lineRenderer.SetPosition(i, currentPosition); //设置线的位置}}void DrawRopeNoWaves() //绘制无波动的绳索{m_lineRenderer.SetPosition(0, grapplingGun.firePoint.position); //设置线的起始位置m_lineRenderer.SetPosition(1, grapplingGun.grapplePoint); //设置线的结束位置}
}
注意:脚本赋予对象后注意赋值,在Rope中的函数曲线绘制以及添加点时一定要注意不能逾越(0,0)以及(1,1)否则会出现钩锁无法发射的问题。
代码赋值部分参数可以参照:


绳索发射曲线样式参照:

最终效果

源码
https://gitcode.net/unity1/unity2d-clawhook

参考
【视频】https://www.youtube.com/watch?v=dnNCVcVS6uw
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!
好了,我是向宇,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关文章:
【Unity造轮子】制作一个简单的2d抓勾效果(类似蜘蛛侠的技能)
前言 欢迎阅读本文,本文将向您介绍如何使用Unity游戏引擎来实现一个简单而有趣的2D抓勾效果,类似于蜘蛛侠的独特能力。抓勾效果是许多动作游戏和平台游戏中的常见元素,给玩家带来了无限的想象和挑战。 不需要担心,即使您是一…...
Unity 人物连招(三段连击)
一: 连招思路 首先人物角色上有三个攻击实例对象 Damage,每一个damage定义了攻击的伤害值,攻击距离,触发器名称,伤害的发起者,攻击持续时间,攻击重置时间,伤害的碰撞框大小等字段: …...
关于WSL以及docker连接adb的坑
结论 WSL可以连接到adb,需要和主机保持一致的adb型号。主机是windows还是macOS的docker没法直接连接到adb设备,只有主机为Linux才可以。其他平台只能通过TCP网络协议。 具体过程 关于WSL连接adb设备 windows安装adb工具(安装可以去官网下…...
python安装第三方包时报错:...\lib\site-packages\pip\_vendor\urllib3\response.py...
安装redis第三方包: pip install redis报错现象: 解决方法:使用以下命令可成功安装 pip install redis -i http://pypi.douban.com/simple --trusted-host pypi.douban.com...
腾讯云从业者认证考试考点——云存储产品
文章目录 存储产品功能云存储产品概述存储产品存储网关存储服务 存储分类按存储方式分按存储频率分 云存储与传统存储的区别功能需求性能需求容量扩展数据共享 云硬盘CBS产品概述归档存储和文件存储归档存储CAS文件存储CFS 对象存储存储网关存储网关的分类 云数据迁移CDM日志服…...
猿辅导Motiff与IXDC达成战略合作,将在UI设计领域推动AI革新更多可能性
近日,“IXDC 2023国际体验设计大会”在北京国家会议中心拉开序幕,3000设计师、1000企业、200全球商业领袖,共襄为期5天的用户体验创新盛会。据了解,此次大会是以“设计领导力”为主题,分享全球设计、科技、商业的前沿趋…...
条件操作符(三目操作符)
比如之前我们想写一个条件判断表达式是这样写的: 用操作符就可以这样写: 应用,比如求两个数的最大值...
(五)Unity开发Vision Pro——FAQ
常见问题 (FAQ) 1.问:我看到在visionOS 模拟器中运行的结果与在硬件上运行的结果不同 请注意,在模拟器中运行时,某些特定于硬件的功能不可用 - 最明显的是 AR 数据。这可能意味着 VisionOS 模拟器中的模拟结果可能与 Vision Pro 耳机上的模…...
GitOps 与 DevOps:了解关键差异,为企业做出最佳选择
在软件开发领域,GitOps 和 DevOps 是加强协作和实现软件交付流程自动化的重要技术。虽然这两种模式都旨在提高软件开发生命周期的效率,但它们的核心原则和实施方式却各不相同。 本篇文章将帮助您了解 GitOps 和 DevOps 之间的差异、它们的工作流程&am…...
Java实现Word文档转PDF,PDF转Word,PDF转Excel,PDF转换工具
前言 java实现word文档转PDF,PDF转word 解决只能转换4页问题 解决每页头部存在水印问题 实现 引入依赖 <dependency><groupId>com.documents4j</groupId><artifactId>documents4j-local</artifactId><version>1.0.3</ve…...
Docker部署ES服务,全量同步的时候内存爆炸,ES自动关闭,CPU100%
问题 使用canal-adapter全量同步(参考Canal Adapter1.1.5版本API操作服务,手动同步数据(4))的时候 小批量数据可以正常运行(几千条)只要数据量一大(上万条),…...
Python——添加照片边框
原图: 添加边框后: 添加边框会读取照片的exif信息如时间、相机型号、品牌以及快门焦段等信息,将他们显示在下面的边框中。 获取当前py文件路径 import os #get path that py file located def Get_Currentpath():file_path os.path.abspa…...
《高性能MySQL》——查询性能优化(笔记)
文章目录 六、查询性能优化6.1 查询为什么会慢6.2 慢查询基础:优化数据访问6.2.1 是否向数据库请求了不需要的数据查询不需要的记录多表关联时返回全部列总是取出全部列重复查询相同的数据 6.2.2 MySQL 是否在扫描额外的记录响应时间扫描的行数与返回的行数扫描的行…...
【Linux操作系统】编译过程中遇到的问题-为什么加-c?执行文件提示无法执行二进制文件?main函数参数argc和*argv[]的作用和理解?
在使用GCC编译器进行程序开发时,我们经常会遇到一些编译过程中的问题, 比如为什么要加上"-c"选项,以及为什么生成的可执行文件无法执行等问题。 本篇博客将详细介绍这些问题,并给出相应的代码和解释,帮助读者…...
【数据结构与算法——TypeScript】图结构(Graph)
【数据结构与算法——TypeScript】 图结构(Graph) 认识图结构以及特性 什么是图? 在计算机程序设计中,图结构 也是一种非常常见的数据结构。 但是,图论其实是一个非常大的话题 认识一下关于图的一些内容 图的抽象数据类型一些算法实现。 什么是图?…...
C语言字符串拷贝函数详解及示例代码
目录 简介字符串拷贝函数 strcpy字符串拷贝函数 strcpy_s使用示例注意事项结束语 1. 简介 字符串拷贝是C语言中常用的操作之一。当需要将一个字符串复制到另一个字符串数组中时,可以使用字符串拷贝函数来实现。C语言提供了多种字符串拷贝函数,其中最常…...
IntelliJ IDEA热部署:JRebel插件的安装与使用
热部署 概述JRebel 概述 热部署,指修改代码后,无需停止应用程序,即可使修改后的代码生效,其有利于提高开发效率。 热部署方式: 手动热部署:修改代码后,重新编译项目,然后启动应用程…...
iTOP-3568开发板使用OpenCV处理图像-颜色转换
本小节代码在配套资料“iTOP-3568 开发板\03_【iTOP-RK3568 开发板】指南教程 \04_OpenCV 开发配套资料\05”目录下,如下图所示: cv2.cvtColor()函数功能: 将一幅图像从一个色彩空间转换到另一个色彩空间。 函数原型: cv2.cvt…...
Python技巧----解压序列/可迭代对象赋值给多个变量
1 、解压序列赋值给多个变量 我们这里说的不是正常情况的一一赋值比如下面 >>> data = [ ACME, 5, 9, (2012, 12, 1) ] >>> name, shares, price, date = data >>> name ACME...
16.3.2 【Linux】程序的管理
程序之间是可以互相控制的。举例来说,你可以关闭、重新启动服务器软件,服务器软件本身是个程序, 你既然可以让她关闭或启动,当然就是可以控制该程序。 使用kill-l或者是man 7 signal可以查询到有多少个signal。主要的讯号代号与名…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
