Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【虚幻引擎】实现锁定敌人与切换锁定

【虚幻引擎】实现锁定敌人与切换锁定

作者头像
六月丶
发布于 2022-12-26 10:08:10
发布于 2022-12-26 10:08:10
3.8K30
代码可运行
举报
文章被收录于专栏:六月-游戏开发六月-游戏开发
运行总次数:0
代码可运行

实现效果

https://hctra.cn/usr/uploads/2022/09/3090895458.mp4

实现思路

锁定目标

用一个球形碰撞体来感知哪些敌人可锁定,碰撞体平时关闭,当玩家按下锁定键时,刷新碰撞体检测并开启一帧,在下一帧就知道哪些目标可锁定了,然后从这些可锁定的目标中找一个与摄像机正前方向量夹角最小的敌人作为锁定对象。

锁定目标后,让玩家视角固定,自身旋转实时朝向追踪敌人方向。

切换锁定目标

同样的,当玩家按下左或右键时,检测碰撞体开启一帧,然后在第二帧从除了当前锁定目标外所有可锁定敌人中,获取在玩家视角左/右边获取夹角最接近的敌人。夹角可以通过两向量点乘获取,然后通过公式:sign = (v1n.X - v1n.Y) * (v2n.Z - v2n.Y) - (v1n.Z - v1n.Y) * (v2n.X - v2n.Y),根据sign的正负得出是在左边还是右边。

实现步骤

因为我习惯用Lua所以代码部分使用的是Lua,如果你也想使用可以参照这篇文章: sluaunreal插件使用样例 sluaunreal插件的作用这里就不多介绍了,它的github地址是:https://github.com/Te...

首先在玩家身上挂一个碰撞体用于检测可锁定目标:

然后给也敌人挂上碰撞体,碰撞体的ObjectType为Enemy,玩家的检测碰撞体预设为只与Enemy发生Overlap:

最好是做成CollisionPresets,这里我就简单的手动调下。

核心代码

当按下锁定键时调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 尝试将视角锁定一个敌人
function cls:CheckLockEnemy_()
    self.enlockEnemyList_ = {}
    self:RefreshCheckEnemyCol_()  --开启一帧碰撞检测
    self:DelayCall(0.01, function()
        local cmpFunc = function(cAngle, angle)
            if angle == nil or math.abs(cAngle) < math.abs(angle) then
                return true
            end
            return false
        end
        local lockEnemy = self:GetLockEnemyByCmp_(cmpFunc)
        if lockEnemy ~= nil then
            self:LockEnemy_(lockEnemy)
        end
    end)
end

当按下切换左/右目标键时,调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 切换锁定目标
    if (key == GE.InputKey.Right or key == GE.InputKey.Left) and self.viewType_bp == CharacterEnums.ViewType.LockEnemy then
        self:RefreshCheckEnemyCol_()
        local cmpFunc = nil
        if key == GE.InputKey.Right then
            cmpFunc = function(cAngle, angle, cActor)
                if angle == nil then
                    if cActor ~= self.lockEnemy_ and cAngle < 0 then
                        return true
                    else
                        return false
                    end
                end
                if cAngle < 0 and cAngle > angle and cActor ~= self.lockEnemy_ then
                    return true
                end
                return false
            end
        else
            cmpFunc = function(cAngle, angle, cActor)
                if angle == nil then
                    if cActor ~= self.lockEnemy_  and cAngle > 0 then
                        return true
                    else
                        return false
                    end
                end
                if cAngle > 0 and cAngle < angle and cActor ~= self.lockEnemy_ then
                    return true
                end
                return false
            end
        end
        self:DelayCall(0.01, function()
            local lockEnemy = self:GetLockEnemyByCmp_(cmpFunc)
            if lockEnemy ~= nil then
                self:LockEnemy_(lockEnemy)
            end
        end)
    end

在Tick中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function cls:ReceiveTick(deltaTime)
    self:BP_Tick(deltaTime)
    -- 如果锁视角,就根据目标手动刷新角色旋转
    if self.viewType_bp == CharacterEnums.ViewType.LockEnemy then
        local selfLoc, enemyLoc = GF:GetActorLocation(self), GF:GetActorLocation(self.lockEnemy_)
        if GF:IsActorValid(self.lockEnemy_) == false or self.lockEnemy_:IsDeath() or 
                GF:VSize(selfLoc - enemyLoc) > self.lockEnemyDistance_bp then
            self:UnLockEnemy_()
        else
            self:UpdateRotationByTarget_(self.lockEnemy_)
        end
    end
end

供调用的功能函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 刷新检测碰撞体
function cls:RefreshCheckEnemyCol_()
    self["bp_EnemyCheckCol"]:SetCollisionEnabled(GE.CollisionEnabled.QueryOnly)
    local radius = self["bp_EnemyCheckCol"]:GetScaledSphereRadius()
    self["bp_EnemyCheckCol"]:SetSphereRadius(0, true)
    self["bp_EnemyCheckCol"]:SetSphereRadius(radius, true)
    self["bp_EnemyCheckCol"]:SetCollisionEnabled(GE.CollisionEnabled.NoCollision)
end
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 锁定目标
function cls:LockEnemy_(lockEnemy)
    self.viewType_bp = CharacterEnums.ViewType.LockEnemy
    if self.lockEnemy_ then
        GD:Post(self, self.lockEnemy_, GEVT.LOCK_ENEMY, false)
    end
    GD:Post(self, lockEnemy, GEVT.LOCK_ENEMY, true)
    self.lockEnemy_ = lockEnemy
    self["bp_SpringArm"].bUsePawnControlRotation = false
    self.movementComp_.bOrientRotationToMovement = false
end
-- 解锁
function cls:UnLockEnemy_()
    self.viewType_bp = CharacterEnums.ViewType.Follow
    GD:Post(self, self.lockEnemy_, GEVT.LOCK_ENEMY, false)
    self.lockEnemy_ = nil
    self["bp_SpringArm"].bUsePawnControlRotation = true
    self.movementComp_.bOrientRotationToMovement = true
end

当发生碰撞时,将目标装入表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
self["bp_EnemyCheckCol"].OnComponentBeginOverlap:Add(function(overlappedComponent, otherActor, otherComp, otherBodyIndex, bFromSweep, sweepResult)
        self.enlockEnemyList_[otherActor] = otherActor
    end)

夹角计算:

(GF库:)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 计算目标对象与自身的夹角
function cls:GetTargetActorAngle(selfActor, targetActor)
    local v1 = self:GetForwardVector(self:GetActorRotation(selfActor))
    local tLoc, sLoc = self:GetActorLocation(targetActor), self:GetActorLocation(selfActor)
    tLoc.Z, sLoc.Z = 0, 0
    local v2 = tLoc - sLoc
    v1, v2 = self:Normal(v1), self:Normal(v2)
    local rad = math.acos(self:Dot_VectorVector(v1, v2))
    local angle = KismetMathLibrary:RadiansToDegrees(rad)
    return angle
end

-- 计算目标对象与自身的夹角(带正负)
function cls:GetTargetActorAngleHaveSign(selfActor, targetActor)
    local v1 = self:GetForwardVector(self:GetActorRotation(selfActor))
    local tLoc, sLoc = self:GetActorLocation(targetActor), self:GetActorLocation(selfActor)
    tLoc.Z, sLoc.Z = 0, 0
    local v2 = tLoc - sLoc
    local v1n, v2n = self:Normal(v1), self:Normal(v2)
    local rad = math.acos(self:Dot_VectorVector(v1n, v2n))
    local angle = KismetMathLibrary:RadiansToDegrees(rad)
    local sign = (v1n.X - v1n.Y) * (v2n.Z - v2n.Y) - (v1n.Z - v1n.Y) * (v2n.X - v2n.Y)
    -- print("hcDel sign angle = ", angle, v1n.X, v1n.Y, v1n.Z, v2n.X, v2n.Y, v2n.Z)
    if sign < 0 then
        angle = -1 * angle
    end

    return angle
end
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- 看向目标旋转
function cls:UpdateRotationByTarget_(target)
    local curRot = GF:GetActorRotation(self)
    local tarRot = GF:FindLookAtRotation(GF:GetActorLocation(self), GF:GetActorLocation(target))
    curRot.Roll, curRot.Pitch = 0, 0
    tarRot.Roll, tarRot.Pitch = 0, 0
    local newRot = GF:RInterpTo(curRot, tarRot, GF:GetDeltaTime(), 5)
    GF:SetActorRotation(self, newRot, false)
end
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022 年 09 月,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
3 条评论
热度
最新
请问,锁定后敌人身上出现的白色锁定标记要怎么实现呢
请问,锁定后敌人身上出现的白色锁定标记要怎么实现呢
22点赞举报
创建一个widget放一张标志图片,然后在敌人的Mesh下放widgetComponent,widgetclass设上,space设为screen,默认隐藏这个widget,当玩家锁定该敌人时会向他发送事件,监听这个事件,根据事件参数显示或隐藏这个widget
创建一个widget放一张标志图片,然后在敌人的Mesh下放widgetComponent,widgetclass设上,space设为screen,默认隐藏这个widget,当玩家锁定该敌人时会向他发送事件,监听这个事件,根据事件参数显示或隐藏这个widget
回复回复1举报
懂了
懂了
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
【虚幻引擎】实现惊险闪避触发时停效果
https://hctra.cn/file/video/avoidTimeStop.mp4
六月丶
2022/12/26
6120
【虚幻引擎】实现惊险闪避触发时停效果
【虚幻引擎】简单三连击实现
https://hctra.cn/usr/uploads/2022/09/3849803929.mp4
六月丶
2022/12/26
5410
【虚幻引擎】简单三连击实现
【虚幻引擎】实现类LOL缓慢扣血血条
分两个进度条重叠放, 一个在前面显示深红的真血条,但是背景条完全透明; 一个在背后显示淡红的跟随血条,背景条不透明。
六月丶
2022/12/26
7750
【虚幻引擎】实现类LOL缓慢扣血血条
使用GDAL实现DEM的地貌晕渲图(一)
以前一直以为对DEM的渲染就是简单的根据DEM的高度不同赋予不同的颜色就可以。后来实际这么做的时候获取的效果跟别的软件相比,根本体现不出地形起伏的变化。如果要体现出地形的起伏变化,需要得到地貌晕渲图才行。晕渲法假设地形接受固定于某一位置光源的平行光线,随坡面与光源方向的夹角不同,产生不同色调明暗效果。 根据文献[1][2],可以通过计算DEM格网点的法向量与日照方向的的夹角,来确定该格网点的像素值。
charlee44
2019/08/13
1.2K0
等了那么久,终于等到新游戏啦!大炮英雄Cocos Creator实现,关注获取代码!
摆放一个背景图,在背景图上添加背景地面、开始按钮、4个角色选择按钮、游戏logo。
张晓衡
2019/10/22
1.1K0
等了那么久,终于等到新游戏啦!大炮英雄Cocos Creator实现,关注获取代码!
Rxjs 响应式编程-第三章: 构建并发程序
并发是正确有效地同时做几件事的艺术。为了实现这一目标,我们构建我们的程序来利用时间,以最有效的方式一起运行任务。 应用程序中的日常并发示例包括在其他活动发生时保持用户界面响应,有效地处理数百个客户的订单。
frontoldman
2019/09/03
3.9K0
Rxjs 响应式编程-第三章: 构建并发程序
Python——极客战记codecombat关卡代码
花了一段时间的峰会之门代码公布出来,其中有些地方参考了国外大神。 如有bug,欢迎反馈。
全栈程序员站长
2022/04/15
2.5K0
Python——极客战记codecombat关卡代码
UE4-实现星星球Demo
https://hctra.cn/usr/uploads/2020/12/352501977.mp4
六月丶
2022/12/26
1.7K0
UE4-实现星星球Demo
个人塔防游戏Demo开发思路(UE4)
游戏为本人毕业设计,功能实现较为简陋,这里只是简要描述下开发思路,不包含深入的Gameplay框架分析,项目可无缝升级至UE4.26,素材全部来源于虚幻商城与互联网。完整项目下载,提取码:demo 游戏主体逻辑采用蓝图系统实现,支持多平台运行,包含存档功能,进入游戏后玩家可以在预先设置好的摆放位置购买和升级防御塔。游戏共有5波敌人,包括最终的BOSS关卡。在击败BOSS通关后即可进入无尽模式,此时游戏难度会不断提高,直到游戏结束。
LonelyEnderman
2023/08/09
1.3K0
个人塔防游戏Demo开发思路(UE4)
黑客帝国「上映」了,是用虚幻引擎5在PS5上跑的
本周五,由 Epic Games 发行的虚幻引擎 5 体验上线了。以华纳兄弟公司电影《黑客帝国》为背景,由拉娜 · 沃卓斯基担任编剧和导演,基努 · 里维斯和凯瑞 - 安 · 莫斯再次饰演 Neo 和 Trinity,并在虚拟世界中扮演了自己在电影中的角色。
机器之心
2021/12/13
9770
黑客帝国「上映」了,是用虚幻引擎5在PS5上跑的
未来能取代Python和JavaScript 的程序语言会是什么?
Python 和 JavaScript 是目前最火的两大编程语言,但是 2020 年,什么编程语言将会取而代之呢?
用户7886150
2020/11/20
7170
“AS3.0高级动画编程”学习:第二章转向行为(上)
因为这一章的内容基本上都是涉及向量的,先来一个2D向量类:Vector2D.as (再次强烈建议不熟悉向量运算的童鞋,先回去恶补一下高等数学-07章空间解释几何与向量代数.pdf) package { import flash.display.Graphics; public class Vector2D { private var _x:Number; private var _y:Number; //构造函数 public function Vector2D(x:Number
菩提树下的杨过
2018/01/23
1.4K0
“AS3.0高级动画编程”学习:第二章转向行为(上)
未来有望干掉 Python 和 JavaScript 的编程语言
Python 和 JavaScript 是两门非常有影响力的编程语言,二者都是我们在打造跨平台应用时会用到的主流语言。由于 Python 和 JavaScript 都是脚本语言,因此它们有很多共同的特性,都需要解释器来运行,都是动态类型,都支持自动内存管理,都可以调用 eval()来执行脚本等等脚本语言所共有的特性。在过去这些年来,Python 和 JavaScript 占据着主导型地位,但有句话说得好,长江后浪推前浪,青出于蓝胜于蓝。如果将来有一天,Python 和 JavaScript 被打败赶下榜单,那这个挑战者会是谁呢?退休的软件工程师 Richard Kenneth Eng 为我们分享了他的看法,罗列了那些他认为有望打败 Python 和 JavaScript 的编程语言。
逆锋起笔
2020/02/21
6240
未来有望干掉 Python 和 JavaScript 的编程语言
SAP数据更新的触发
应用层运行着DIALOG进程,每个DIALOG进程绑定一个数据库进程,DIALOG进程与GUI进行通信,每次GUI向应用服务器发送请求时都会通过dispatcher服务为每个GUI的请求分配一个Dialog进程.一个程序运行时,GUI与Dialog进行需要多次通信,每次通信使用的Dialog进程不一定相同,在Dialog进程将控制权转给前台的GUI时,由于Dialog进程同数据库进程绑定,会触发一个隐式数据库提交(COMMIT WORK),如果在Dialog进程发生A类型错误,则触发隐式的数据库回滚(Rollback) SAP LUW SAP LUW是DB LUW的一个增强,受体系结构限制,SAP程序每次屏幕切换时(控制权从后台DIALOG进程转移到前台GUI的Session),都会触发一个隐式的数据库提交,一个程序在运行是会产生多个DB 的LUW,这样无法做到全部提交或全部回滚,在某些业务场景下,这种事务的提交机制不足以保证数据的一致性,为此有有了SAP LUW机制.SAP LUW是一种延迟执行的技术,它将本来需要执行的程序块,记录下来.记录的位置在内存或DB Table中,如perform on commit 会记录到内存中,update Funciton module即可以记录到内存也可以记录到VBMOD 和VBMOD表中.系统在执行COMMIT WORK的时候会查询记录,真正执行需要运行的代码,COMMIT WORK一般在最后一个屏幕执行,这样就实现了将跨屏幕的数据更新逻辑绑定到一个DB LUW中,实现复杂情况数据更新的一致性 SAP LUW的绑定方式 CALL FUNCTION...IN UPDATE TASK, 该种方式需要Funciton类型为Update Module类型,同时在调用时使用IN UPDATE TASK参数. 在程序调用 Update Module进行更新时分为本地和非本地 非本地方式: 注册的更新函数记录在VBMOD 和VBMOD表中,COMMIT WORK 时更新操作在UPDATE进程中执行,此时调用程序不等待被调用函数的返回,使用的为异步方式.如果使用COMMIT WORK AND WAIT,此时调用程序等待被调用函数的返回,使用的为同步方式. 本地方式 在调用函数前需要执行 SET UPDATE TASK LOCAL. 这样所有在该语句后使用CALL FUNCTION...IN UPDATE TASK注册的更新函数不会记录到数据库中,而是记录在内存中,在Commit work之后,会从内存取得待执行的函数,在同一个Dialog进程中执行数据的更新,本地方式更新采用的是同步方式,即使在Commit work后指定了and wait参数,仍然是同步执行. 在使用COMMIT WORK之后 SET UPDATE TASK LOCAL的效果会被清除掉,如果COMMIT WORK后注册的更新函数仍然需要采用本地方式,需要再执行一次 SET UPDATE TASK LOCAL语句. 优缺点对比 本地方式不将待执行的更新函数写到数据表中,减少了I/O操作,效率上较高,但由于采用的是同步方式,程序需等待更新结果,用户交互时的会感觉程序运行较慢 非本地方式会将更新结果记录到数据表中,可以通过SM13查看更新情况,同时由于可以进行异步更新,用户交互时感觉会比较快 CALL FUNCTION... IN BACKGROUND TASK DESTINATION, 是一种对RFC函数进行事务绑定的方式
matinal
2020/11/27
7550
SAP数据更新的触发
PCLVisualizer可视化类
PCLVisualizer可视化类是PCL中功能最全的可视化类,与CloudViewer可视化类相比,PCLVisualizer使用起来更为复杂,但该类具有更全面的功能,如显示法线、绘制多种形状和多个视口。本小节将通过示例代码演示PCLVisualizer可视化类的功能,从显示单个点云开始。大多数示例代码都是用于创建点云并可视化其某些特征
点云PCL博主
2019/07/31
2K0
PCLVisualizer可视化类
图像抠图算法学习 - Shared Sampling for Real-Time Alpha Matting
本文探讨了基于深度学习的图像分割算法,并分析了其优缺点。首先介绍了基于生成对抗网络(GAN)的图像分割算法,然后阐述了基于深度学习的图像分割算法在处理图像中的前景和背景之间的区别。针对前景和背景之间的区别,提出了一种基于深度学习的方法,该方法能够准确地分割前景和背景。同时,该方法也可以用于其他需要前景和背景分割的任务。
用户1138785
2018/01/03
2.5K0
图像抠图算法学习  -  Shared Sampling for Real-Time Alpha Matting
lua Standard Libraries
The standard Lua libraries provide useful functions that are implemented directly through the C API. Some of these functions provide essential services to the language (e.g., type and getmetatable); others provide access to “outside” services (e.g., I/O);
晚晴幽草轩轩主
2018/03/27
1.2K0
Canvas系列(14):实战-小球碰撞
两小球碰撞是Canvas非常经典的案例,他是一个很简单的需求,但做起来却非常复杂。
kai666666
2020/10/19
1.9K0
Canvas系列(14):实战-小球碰撞
数美滑块验证码分析
数美验证码官网:https://www.ishumei.com/trial/captcha.html
李玺
2021/11/22
1.6K0
数美滑块验证码分析
高等应用数学问题MATLAB求解.第一,二章
这里说了,就是装maple的锅,估计是32位的Maple替换了原本64位matlab自带的maple库,我还能遇到这种事情???
云深无际
2021/09/14
9950
高等应用数学问题MATLAB求解.第一,二章
相关推荐
【虚幻引擎】实现惊险闪避触发时停效果
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验