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

UE学习日志#14 GAS--ASC源码简要分析10 GC相关

注:1.这个分类是按照源码里的注释分类的

2.本篇是通读并给出一些注释形式的,并不涉及结构性的分析

3.看之前要对UE的GAS系统的定义有初步了解

4.因为都是接口函数,有些没细看的研究那一部分的时候会细看

1  一些接口函数,但是注释说不要直接调用要通过GameplayCueManager调用

// Do not call these functions directly, call the wrappers on GameplayCueManager insteadUFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueExecuted_FromSpec(const FGameplayEffectSpecForRPC Spec, FPredictionKey PredictionKey) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueExecuted(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCuesExecuted(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueExecuted_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCuesExecuted_WithParams(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueAdded(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueAdded_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters Parameters) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueAddedAndWhileActive_FromSpec(const FGameplayEffectSpecForRPC& Spec, FPredictionKey PredictionKey) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCueAddedAndWhileActive_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;UFUNCTION(NetMulticast, unreliable)void NetMulticast_InvokeGameplayCuesAddedAndWhileActive_WithParams(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;

2 ExecuteGameplayCue相关

注释翻译:GameplayCues也可以独立出现,这些函数接受一个可选的效果上下文,用于传递命中结果等信息

一个是传入GEContextHandle版本的,一个是传入FGameplayCueParameters版本的

声明如下:

/** GameplayCues can also come on their own. These take an optional effect context to pass through hit result, etc */void ExecuteGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());void ExecuteGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);

实现就是调用GameplayCueManager里的函数:

void UAbilitySystemComponent::ExecuteGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext)
{// Send to the wrapper on the cue managerUAbilitySystemGlobals::Get().GetGameplayCueManager()->InvokeGameplayCueExecuted(this, GameplayCueTag, ScopedPredictionKey, EffectContext);
}void UAbilitySystemComponent::ExecuteGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters)
{// Send to the wrapper on the cue managerUAbilitySystemGlobals::Get().GetGameplayCueManager()->InvokeGameplayCueExecuted_WithParams(this, GameplayCueTag, ScopedPredictionKey, GameplayCueParameters);
}

 先大致看下实现可以发现逻辑都一样,都是先检查有效性,再构造FGameplayCuePendingExecute PendingCue,再调用AddPendingCueExecuteInternal,唯一不一样的地方就是初始化PendingCue.CueParameters这个参数的方式不一样,WithParams版本的很简单,就是直接赋值,下面去找下InitGameplayCueParameters这个函数,里边重点就这一行:

CueParameters.EffectContext = EffectContext;

这里贴的实现: 

void UGameplayCueManager::InvokeGameplayCueExecuted(UAbilitySystemComponent* OwningComponent, const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext)
{if (EnableSuppressCuesOnGameplayCueManager && OwningComponent && OwningComponent->bSuppressGameplayCues){return;}if (OwningComponent){FGameplayCuePendingExecute PendingCue;PendingCue.PayloadType = EGameplayCuePayloadType::CueParameters;PendingCue.GameplayCueTags.Add(GameplayCueTag);PendingCue.OwningComponent = OwningComponent;UAbilitySystemGlobals::Get().InitGameplayCueParameters(PendingCue.CueParameters, EffectContext);PendingCue.PredictionKey = PredictionKey;AddPendingCueExecuteInternal(PendingCue);}
}void UGameplayCueManager::InvokeGameplayCueExecuted_WithParams(UAbilitySystemComponent* OwningComponent, const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters)
{if (EnableSuppressCuesOnGameplayCueManager && OwningComponent && OwningComponent->bSuppressGameplayCues){return;}if (OwningComponent){FGameplayCuePendingExecute PendingCue;PendingCue.PayloadType = EGameplayCuePayloadType::CueParameters;PendingCue.GameplayCueTags.Add(GameplayCueTag);PendingCue.OwningComponent = OwningComponent;PendingCue.CueParameters = GameplayCueParameters;PendingCue.PredictionKey = PredictionKey;AddPendingCueExecuteInternal(PendingCue);}
}

顺着思路看AddPendingCueExecuteInternal:

发现就是把他加入了执行队列,但是没有真正执行

void UGameplayCueManager::AddPendingCueExecuteInternal(FGameplayCuePendingExecute& PendingCue)
{if (ProcessPendingCueExecute(PendingCue)){PendingExecuteCues.Add(PendingCue);}if (GameplayCueSendContextCount == 0){// Not in a context, flush nowFlushPendingCues();}
}

 再去看FlushPendingCues这个函数:

函数体太长了,核心就是调用这两个函数:

诶惊奇的发现就是前面第一部分里不让你直接调用的接口函数,找了一圈RepInterface也是调用接口函数,所以最后除了各个函数的条件判断不太一样,都是调用的ASC中的InvokeGameplayCueEvent

RepInterface->Call_InvokeGameplayCueExecuted_WithParams
PendingCue.OwningComponent->InvokeGameplayCueEvent

这里的RepInterface:

IAbilitySystemReplicationProxyInterface* RepInterface = PendingCue.OwningComponent->GetReplicationInterface();

再顺着看Invoke这个函数,发现都调用GameplayCueManager的HandleGameplayCue

UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue

GC中的HandleGameplayCue会先将标签翻译,然后路由,路由中的具体处理:

CueSet和Interface调用HandleGameplayCue,其中的GameplayCueInterface:

Cast<IGameplayCueInterface>(TargetActor) 
	// Give the global set a chanceif (bAcceptsCue && !(Options & EGameplayCueExecutionOptions::IgnoreNotifies)){RuntimeGameplayCueObjectLibrary.CueSet->HandleGameplayCue(TargetActor, GameplayCueTag, EventType, Parameters);}// Use the interface even if it's not in the mapif (GameplayCueInterface && bAcceptsCue){GameplayCueInterface->HandleGameplayCue(TargetActor, GameplayCueTag, EventType, Parameters);}

Interface中的Handle会处理函数列表,最后转到默认处理函数,子类可以实现

而CueSet中调用这个函数:

UGameplayCueSet::HandleGameplayCueNotify_Internal

会分UGameplayCueNotify_Static和AGameplayCueNotify_Actor处理具体逻辑,到这就各种信息检索完了,进入具体的处理逻辑,之后就要进入这两个类里看了,这里就不继续看了

3 AddGameplayCue

/** Add a persistent gameplay cue */void AddGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());void AddGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);

套娃到这里:

这个函数大体逻辑:

  1. 检查是否是服务器

  2. 检查是否已经存在该提示:避免重复添加

  3. 强制网络同步:确保客户端能够接收到最新的游戏玩法提示

  4. 添加GC到容器

  5. 处理混合复制模式:根据复制模式调整预测Key

  6. 调用RPC播放激活事件:通过RPC将游戏玩法提示同步到客户端

  7. 触发服务器端事件:在服务器端触发 WhileActive 事件

  8. 客户端预测逻辑:在客户端预测性地添加GC,并触发事件

void UAbilitySystemComponent::AddGameplayCue_Internal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters, FActiveGameplayCueContainer& GameplayCueContainer)
{if (IsOwnerActorAuthoritative()){const bool bWasInList = GameplayCueContainer.HasCue(GameplayCueTag);ForceReplication();GameplayCueContainer.AddCue(GameplayCueTag, ScopedPredictionKey, GameplayCueParameters);// For mixed minimal replication mode, we do NOT want the owning client to play the OnActive event through this RPC, since it will get the full replicated // GE in its AGE array. Generate a server-side prediction key for it, which it will look for on the _Implementation function and ignore. (<--- Original Hack){FPredictionKey PredictionKeyForRPC = ScopedPredictionKey; // Key we send for RPC. Start with the regular old ScopedPredictionKey// Special stuff for mixed replication modeif (ReplicationMode == EGameplayEffectReplicationMode::Mixed){if (GameplayCueContainer.bMinimalReplication){// For *replicated to sim proxies only* container, Create a Server Initiated PK to avoid double playing on the auto proxy in mixed replication mode (Original Hack)PredictionKeyForRPC = FPredictionKey::CreateNewServerInitiatedKey(this);}else{// For "replicated to everyone" cue container, we need to clear server replicated prediction keys, or else they will trip the same absorption code that we added for the first hack above.// Its ok to just throw out a server replicated prediction key because (outside of mixed replication mode) it will not affect what the client does in NetMulticast_InvokeGameplayCueAdded_WithParams_Implementation// (E.g, the client only skips the InvokeCall if the key is locally generated, not for server generated ones anways)if (ScopedPredictionKey.IsServerInitiatedKey()){PredictionKeyForRPC = FPredictionKey();}}}// Finally, call the RPC to play the OnActive eventif (IAbilitySystemReplicationProxyInterface* ReplicationInterface = GetReplicationInterface()){ReplicationInterface->Call_InvokeGameplayCueAdded_WithParams(GameplayCueTag, PredictionKeyForRPC, GameplayCueParameters);}}if (!bWasInList){// Call on server here, clients get it from repnotifyInvokeGameplayCueEvent(GameplayCueTag, EGameplayCueEvent::WhileActive, GameplayCueParameters);}}else if (ScopedPredictionKey.IsLocalClientKey()){GameplayCueContainer.PredictiveAdd(GameplayCueTag, ScopedPredictionKey);// Allow for predictive gameplaycue events? Needs more thoughtInvokeGameplayCueEvent(GameplayCueTag, EGameplayCueEvent::OnActive, GameplayCueParameters);InvokeGameplayCueEvent(GameplayCueTag, EGameplayCueEvent::WhileActive, GameplayCueParameters);}
}

4 RemoveGameplayCue

/** Remove a persistent gameplay cue */void RemoveGameplayCue(const FGameplayTag GameplayCueTag);

 去到了Container里处理Remove,这里就不深入了,具体研究GC会继续看

void UAbilitySystemComponent::RemoveGameplayCue_Internal(const FGameplayTag GameplayCueTag, FActiveGameplayCueContainer& GameplayCueContainer)
{if (IsOwnerActorAuthoritative()){GameplayCueContainer.RemoveCue(GameplayCueTag);}else if (ScopedPredictionKey.IsLocalClientKey()){GameplayCueContainer.PredictiveRemove(GameplayCueTag);}
}

总结:1.以上就是几个关键的函数,但都是偏向的都是调用的整体逻辑,没有深入具体的实现,留坑()

2.按着我看的顺序来的,并非实际调用顺序

相关文章:

UE学习日志#14 GAS--ASC源码简要分析10 GC相关

注&#xff1a;1.这个分类是按照源码里的注释分类的 2.本篇是通读并给出一些注释形式的&#xff0c;并不涉及结构性的分析 3.看之前要对UE的GAS系统的定义有初步了解 4.因为都是接口函数&#xff0c;有些没细看的研究那一部分的时候会细看 1 一些接口函数&#xff0c;但是…...

使用Python和Qt6创建GUI应用程序--关于Qt的一点介绍

关于Qt的一点介绍 Qt是一个免费的开源部件工具包&#xff0c;用于创建跨平台GUI应用程序&#xff0c;允许应用程序从Windows瞄准多个平台&#xff0c;macOS&#xff0c; Linux和Android的单一代码库。但是Qt不仅仅是一个Widget工具箱和功能内置支持多媒体&#xff0c;数据库&am…...

C#@符号在string.Format方法中作用

本文详解@符号在string.Format方法中作用。...

Next.js 14 TS 中使用jwt 和 App Router 进行管理

jwt是一个很基础的工作。但是因为架构不一样&#xff0c;就算是相同的架构&#xff0c;版本不一样&#xff0c;加jwt都会有一定的差别。现在我们的项目是Next.js 14 TS 的 App Router项目&#xff08;就是没有pages那种&#xff09;&#xff0c;添加jwt的步骤&#xff1a; 1、…...

【贪心算法】洛谷P1090 合并果子 / [USACO06NOV] Fence Repair G

2025 - 01 - 21 - 第 45 篇 【洛谷】贪心算法题单 -【 贪心算法】 - 【学习笔记】 作者(Author): 郑龙浩 / 仟濹(CSND账号名) 洛谷 P1090[NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G 【贪心算法】 文章目录 洛谷 P1090[NOIP2004 提高组] 合并果子 / [USACO06…...

Windows11无法打开Windows安全中心主界面

​# 问题描述 安全中心无法打卡主界面&#xff0c;并弹出“需要使用新应用以打开此windowsdefender连接”. 解决方法 以管理员权限打开PowerShell&#xff0c;推荐使用快捷键win x打开快捷界面&#xff0c;选择Windows终端&#xff08;管理员&#xff09;&#xff0c;并在终…...

下载arm架构的deb包的方法

在ARM板上操作 如果你是在arm板上使用apt安装和下载包&#xff0c;那么安装过的包会在以下路径里&#xff1a; /var/cache/apt/archives只需要复制出来就可以 如果只下载不安装&#xff0c;可以使用命令 sudo apt-get -d install package_name:arm64 # 如果是32位&#xff0…...

【Day29 LeetCode】动态规划DP

一、动态规划DP 1、不同路径 62 首先是dp数组&#xff0c;dp[i][j]表示从起点(0, 0)到达当前位置(i, j)的路径数&#xff0c;转移方程从只能向下和向右移动可知&#xff0c;初始化边界可直观推出第一行和第一列上的位置只有一条路径。 class Solution { public:int uniquePa…...

5分钟带你获取deepseek api并搭建简易问答应用

目录 1、获取api 2、获取base_url和chat_model 3、配置模型参数 方法一&#xff1a;终端中临时将加入 方法二&#xff1a;创建.env文件 4、 配置client 5、利用deepseek大模型实现简易问答 deepseek-v3是截止博文撰写之日&#xff0c;无论是国内还是国际上发布的大模型中…...

LeetCode题练习与总结:最短无序连续子数组--581

一、题目描述 给你一个整数数组 nums &#xff0c;你需要找出一个 连续子数组 &#xff0c;如果对这个子数组进行升序排序&#xff0c;那么整个数组都会变为升序排序。 请你找出符合题意的 最短 子数组&#xff0c;并输出它的长度。 示例 1&#xff1a; 输入&#xff1a;num…...

探秘 TCP TLP:从背景到实现

回家的路上还讨论了个关于 TCP TLP 的问题&#xff0c;闲着无事缕一缕。本文内容参考自 Tail Loss Probe (TLP): An Algorithm for Fast Recovery of Tail Losses 以及 Linux 内核源码。 TLP&#xff0c;先说缘由。自 TCP 引入 Fast retrans 机制就是为了尽力避免 RTO&#xf…...

linux学习之网络编程

一、两个模型及其对应关系 OSI七层模型 TCP/IP 四层模型 -------------------------------------------------------------------------- 应用层 表示层 ----> …...

scrol家族 offset家族 client家族学习

Scroll 系列属性 scrollTop & scrollLeft scrollTop: 返回元素的内容已向上滚动的部分的高度。scrollLeft: 返回元素的内容已向左滚动的部分的宽度。 scrollHeight & scrollWidth scrollHeight: 返回元素的实际高度&#xff0c;包括由于溢出而在屏幕上不可见的内容…...

css-background-color(transparent)

1.前言 在 CSS 中&#xff0c;background-color 属性用于设置元素的背景颜色。除了基本的颜色值&#xff08;如 red、blue 等&#xff09;和十六进制颜色值&#xff08;如 #FF0000、#0000FF 等&#xff09;&#xff0c;还有一些特殊的属性值可以用来设置背景颜色。 2.backgrou…...

如何将xps文件转换为txt文件?xps转为pdf,pdf转为txt,提取pdf表格并转为txt

文章目录 xps转txt方法一方法二 pdf转txt整页转txt提取pdf表格&#xff0c;并转为txt 总结另外参考XPS文件转换为TXT文件XPS文件转换为PDF文件PDF文件转换为TXT文件提取PDF表格并转为TXT示例代码&#xff08;部分&#xff09; 本文测试代码已上传&#xff0c;路径如下&#xff…...

【Samba】Ubuntu20.04 Windows 共享文件夹

【Samba】Ubuntu20.04 Windows 共享文件夹 前言整体思路检查 Ubuntu 端 和 Windows 网络通信是否正常创建共享文件夹安装并配置 Samba 服务器安装 Samba 服务器创建 Samba 用户编辑 Samba 配置文件重启 Samba 服务器 在 Windows 端 访问 Ubuntu 的共享文件夹 前言 本文基于 Ub…...

gradle和maven的区别以及怎么选择使用它们

目录 区别 1. 配置方式 2. 依赖管理 3. 构建性能 4. 灵活性和扩展性 5. 多项目构建 如何选择使用 选择 Maven 的场景 选择 Gradle 的场景 区别 1. 配置方式 Maven&#xff1a; 使用基于 XML 的 pom.xml 文件进行配置。所有的项目信息、依赖管理、构建插件等都在这个文…...

360大数据面试题及参考答案

数据清理有哪些方法? 数据清理是指发现并纠正数据文件中可识别的错误,包括检查数据一致性,处理无效值和缺失值等。常见的数据清理方法有以下几种: 去重处理:数据中可能存在重复的记录,这不仅会占用存储空间,还可能影响分析结果。通过对比每条记录的关键属性,若所有关键…...

Myeclipse最新版本 C1 2019.4.0

Myeclipse C1 2019.4.0下载地址&#xff1a;链接: https://pan.baidu.com/s/1MbOMLewvAdemoQ4FNfL9pQ 提取码: tmf6 1.1、什么是集成开发环境? ★集成开发环境讲究-站式开发&#xff0c;使用这个工具即可。有提示功能&#xff0c;有自动纠错功能。 ★集成开发环境可以让软件开…...

MySQL 9.2.0 的功能

MySQL 9.2.0 的功能 MySQL 9.2.0 的功能新增、弃用和删除内容如下&#xff1a; 新增功能 权限新增12&#xff1a;引入了CREATE_SPATIAL_REFERENCE_SYSTEM权限&#xff0c;拥有该权限的用户可执行CREATE SPATIAL REFERENCE SYSTEM、CREATE OR REPLACE SPATIAL REFERENCE SYSTEM…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...