37. UE5 RPG创建自定义的Ability Task

在前面的文章中,我们实现了一个火球术的一些基本功能,火球术技能的释放,在技能释放后,播放释放动画,在动画播放到需要释放火球术的位置时,将触发动画通知,在动画通知中触发标签事件,然后再技能中监听事件完成火球术的创建。接下来,我们将继续优化火球术技能,并研究点新的东西。
在上一篇文章中,我们使用了PlayMontageAndWait节点实现蒙太奇的播放,这个节点实现是基于Ability Task(AT)实现的,它的主要特点是可以在技能蓝图中使用异步,我们要通过Ability Task类实现一个存储鼠标点击位置信息的Task,并且可以实现将数据传递到服务器,实现服务器同步播放动画。

创建TargetDataUnderMouse

我们首先实现一个AbilityTask(AT),用于保存触发技能时鼠标拾取的数据。
在这里插入图片描述
将类名称设置为TargetDataUnderMouse
在这里插入图片描述
首先在类里面增加一个静态函数,这个函数用来创建类的实例,也就是我们将节点添加到蓝图,它就会创建一个实例。参数配置我在上一片文章使用播放蒙太奇的节点时也介绍了一下,这里在解释一下。
DisplayName 为在蓝图搜索时,可以直接搜索这个名称,节点上也会显示这个名称。
HidePin 是隐藏一个参数的引脚,设置以后,在蓝图中无法设置它的属性。
DefaultToSelf 将类或者蓝图实例作为默认参数 这两项一起使用,我们就不需要设置OwningAbility的值了,默认设置了蓝图实例。
BlueprintInternalUseOnly 设置了此函数只能在蓝图中使用。

	UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta=(DisplayName = "TargetDataUnderMouse", HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true"))
	static UTargetDataUnderMouse* CreateTargetDataUnderMouse(UGameplayAbility* OwningAbility);

这个静态函数的视线,我们就直接创建一个实例返回

UTargetDataUnderMouse* UTargetDataUnderMouse::CreateTargetDataUnderMouse(UGameplayAbility* OwningAbility)
{
	UTargetDataUnderMouse* MyObj = NewAbilityTask<UTargetDataUnderMouse>(OwningAbility);
	return MyObj;
}

这就是一个最简单的AbilityTask(AT)实现,编译后,我们在技能蓝图中搜索名称,效果如下,右上角的时钟图标代表它是一个异步节点,这是一个没有任何功能的AT,只实现创建,没有其它功能。
在这里插入图片描述
接着,在类里面添加一个委托,看看效果,首先增加一个委托宏,返回一个向量

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMouseTargetDataSignature, const FVector&, Data);

接着定义一个变量 ,作为蓝图可调用的委托

	UPROPERTY(BlueprintAssignable)
	FMouseTargetDataSignature ValidData;

运行打开,会发现右侧多了两个引脚,一个是回调广播后可以执行引脚,另一个则是数据引脚。
在这里插入图片描述
接下来,我们要实现这个委托的广播,并获取到鼠标拾取点位的位置传递给技能实例。为了实现这个功能,我们将覆盖默认的执行函数,这函数将在触发左键引脚时,执行内部的内容。

private:

	virtual void Activate() override;

在实现这里,你不需要调用它的父调用,因为它在父类里面只做了打印,没有执行其它逻辑,但是你如果需要调式的时候可以调用。
在函数中,我们首先要获取到它的PlayerController,因为在PC上面可以去拾取点击位置的坐标,然后使用PC上面的坐标拾取函数去获取结果,然后直接广播出去坐标。

void UTargetDataUnderMouse::Activate()
{
	APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
	FHitResult CursorHit;
	PC->GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);
	ValidData.Broadcast(CursorHit.Location);
}

编译,打开UE,修改技能蓝图,在委托回调里接收到广播以后,绘制调试球体,然后结束技能
在这里插入图片描述
会发现每点击一次,敌人身上生成一个调试模型。
在这里插入图片描述
那么重点来了,如果我们开启两个客户端,一个运行在服务器端,另一个运行在单独客户端
在这里插入图片描述
你会发现,在客户端自己的位置是正确的,但是服务器端显示效果位置不正确,这是因为当前广播的数据没有上传到服务器,所以出现了数据在原点显示的bug,接下来,我们将实现从客户端将数据上传到服务器,解决这个bug。
在这里插入图片描述

使用TargetData传递数据到服务器

首先我们分析一下,为什么出现这个问题,在服务器端的位置为0,在技能被激活后,触发我们制作的Task实例,然后在内部进行鼠标拾取位置,并广播出来。需要同步的数据是在AT激活和返回的拾取坐标,由于是需要客户端提交到服务器端,有网络的延迟,你无法确定哪个数据先被提交成功,比如当前问题就是AT激活时,坐标位置还没有提交到服务器,所以在服务器向后运行时是没有坐标值的。
好在,GAS系统里面想到了这一点,它内置一套TargetData的系统(FGameplayAbilityTargetData)帮助我们实现从客户端提交数据并实现了异步等待数据提交完成再向后执行的逻辑。通过ServerSetReplicatedTargetData()函数将数据上传到服务器,并在服务器生成FAbilityTargetDataSetDelegate委托,并将数据广播出去,在技能里面,通过在AbilityTargetDataMap(Key是技能实例,value是TargetData)获取到实际数据,来运行后续的逻辑。
在服务器端,我们首先绑定TargetSet的委托,这样,如果激活逻辑先上传到服务器,我们可以通过委托的广播来获取数据。如果是数据先到的服务器,在激活时无法获取到委托的广播,我们可以使用CallReplicatedTargetDataDelegateIfSet()函数获取。
双击Shift键,在文件GameplayAbilityTargetTypes.h中,我们可以看到内置TargetData给我们定义了多个格式,每个格式传递的内容也不相同。
在这里插入图片描述
UE的GAS为我们派生三种类型:

  1. FGameplayAbilityTargetData_LocationInfo 这个类用于表示基于位置的目标数据。例如,一个技能可能需要在某个特定的地点释放效果,而不是针对某个特定的角色。FGameplayAbilityTargetData_LocationInfo 包含了这样的位置信息,如世界坐标或其他与位置相关的数据。
  2. FGameplayAbilityTargetData_ActorArray 这个类用于表示基于一组角色的目标数据。如果一个技能需要影响多个角色,那么可以使用这个类。它包含了一个角色数组(通常是 AActor 或其派生类的实例),这样技能就可以对数组中的每个角色应用效果。
  3. FGameplayAbilityTargetData_SingleTargetHit 这个类用于表示单个角色作为目标的数据。当一个技能只影响一个角色时,可以使用这个类。它通常包含了关于这个单一目标的信息,比如该角色的位置、健康状态或其他相关数据。

下面,我就实现客户端的数据提交并实现服务器端的数据接收并处理。
增加一个私有函数,用于内部用于实现数据从客户端提交到服务器端

	//客户端向服务器端提交数据
	void SendMouseCursorData();

在函数内顶部,我们添加一个预测窗口

	//创建一个预测窗口,该窗口允许客户端在不确定服务器响应的情况下,对游戏状态进行预测性更新。
	FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent.Get(), true);

接下来,我们需要创建一个提交的TargetData,这里选择讲一个单一的目标上传,所以创建一个FGameplayAbilityTargetData_SingleTargetHit变量,并将我们从鼠标拾取的结果设置给Data

	//获取鼠标拾取结果
	APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
	FHitResult CursorHit;
	PC->GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);

	//创建需要上传服务器端的TargetData
	FGameplayAbilityTargetData_SingleTargetHit* Data = new FGameplayAbilityTargetData_SingleTargetHit();
	Data->HitResult = CursorHit;

将TargetData上传至服务器端需要它的句柄,我们将创建一个FGameplayAbilityTargetDataHandle 用于存储Data

	//创建TargetData句柄,上传到服务器端需要上传句柄
	FGameplayAbilityTargetDataHandle DataHandle;
	DataHandle.Add(Data);

接着调用上传函数ASC的ServerSetReplicatedTargetData

	//将TargetData上传至服务器端
	AbilitySystemComponent->ServerSetReplicatedTargetData(
		GetAbilitySpecHandle(),
		GetActivationPredictionKey(),
		DataHandle,
		FGameplayTag(),
		AbilitySystemComponent->ScopedPredictionKey);

由于上传的是DataHandle,我们将委托宏的值也修改为DataHandle

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMouseTargetDataSignature, const FGameplayAbilityTargetDataHandle&, DataHandle);

判断当前是否可以触发委托函数,这个触发需要在服务器端通过验证后触发,由于网络限制,这时候都没有执行完成

	//判断服务器端是否通过验证
	if(ShouldBroadcastAbilityTaskDelegates())
	{
		ValidData.Broadcast(DataHandle);
	}

上面,我们实现在客户端数据的发送,接下来,我们将修改Task的Activate()函数,在函数内,我们首先判断当前task是否由本地玩家控制

	//是否由客户端控制
	const bool bIsLocallyControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();

如果是由客户端控制,那么我们将执行数据提交服务器端的逻辑。

	if(bIsLocallyControlled)
	{
		//如果是客户端控制器控制,实现将数据发射到服务器端
		SendMouseCursorData();
	}

如果不是本地控制,那肯定当前是在服务器端运行,我们在服务器端监听数据提交成功。
首先创建两个值,用于实现委托SpecHandle 为当前技能的标示,ActivationPredictionKey 为预测键,用于同步客户端和服务器之间的预测性操作

const FGameplayAbilitySpecHandle SpecHandle = GetAbilitySpecHandle();  
const FPredictionKey ActivationPredictionKey = GetActivationPredictionKey();

接着设置委托,当服务器端接收到目标数据时,这个回调函数会被触发。

AbilitySystemComponent.Get()->AbilityTargetDataSetDelegate(SpecHandle, ActivationPredictionKey).AddUObject(this, &UTargetDataUnderMouse::OnTargetDataReplicatedCallback);

通过调用CallReplicatedTargetDataDelegatesIfSet函数来检查是否已经为特定的SpecHandle和ActivationPredictionKey调用了委托。如果已经调用过,bCalledDelegate将为true。

		//判断在服务器端,上面的委托是否已经广播过
		const bool bCalledDelegate = AbilitySystemComponent.Get()->CallReplicatedTargetDataDelegatesIfSet(SpecHandle, ActivationPredictionKey);

如果当前委托还未广播,我们将调用SetWaitingOnRemotePlayerData()让服务器端正在等待客户端上传目标数据。

		if(!bCalledDelegate)
		{
			//设置服务器端等待PlayerData数据的上传
			SetWaitingOnRemotePlayerData();
		}

接下来就是委托函数的视线,我们先定义一个函数,委托会返回两个值,数据的句柄和激活的标签

	//当数据提交到服务器端后的委托回调
	void OnTargetDataReplicatedCallback(const FGameplayAbilityTargetDataHandle& DataHandle, FGameplayTag ActivationTag);

在回调函数中,我们首先将数据应用到本地客户端,并将缓存的数据清除掉,如果数据通过了验证,则广播数据。

void UTargetDataUnderMouse::OnTargetDataReplicatedCallback(const FGameplayAbilityTargetDataHandle& DataHandle, FGameplayTag ActivationTag)
{
	//通知客户端 服务器端已经接收并处理了从客户端复制的目标数据(将服务器的TargetData应用到客户端,并清除掉缓存)
	AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey());
	//判断服务器端是否通过验证
	if(ShouldBroadcastAbilityTaskDelegates())
	{
		ValidData.Broadcast(DataHandle);
	}
}

还有重要的一项,如果我们要使用TargetData,UE默认是不开启此功能的,我们需要代码开启,在资源管理器StartInitialLoading()函数中开启。

void UMyAssetManager::StartInitialLoading()
{
	Super::StartInitialLoading();

	FMyGameplayTags::InitializeNativeGameplayTags();

	//如果使用TargetData,必须开启此项
	UAbilitySystemGlobals::Get().InitGlobalData();
}

接着,我们打开技能蓝图,修改蓝图,由于Task返回的内容在代码里面修改掉了,现在返回的是TargetDataHandle了,我们通过Get Hit Result from Target Data 节点来获取设置的数据,从里面找到目标的位置数据绘制调试球体。
在这里插入图片描述
修改一下网络模式,并设置一下接口,以监听服务器运行,主窗口将为服务器端,其它窗口为客户端
在这里插入图片描述
然后在客户端上面点击哥布林,发现服务器可以获取到相关的数据了。
在这里插入图片描述
接下来,我把制作的AT的代码放在下面
TargetDataUnderMouse.h

// 版权归暮志未晚所有。

#pragma once

#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "TargetDataUnderMouse.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMouseTargetDataSignature, const FGameplayAbilityTargetDataHandle&, DataHandle);

/**
 * 
 */
UCLASS()
class AURA_API UTargetDataUnderMouse : public UAbilityTask
{
	GENERATED_BODY()

public:

	UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta=(DisplayName = "TargetDataUnderMouse", HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true"))
	static UTargetDataUnderMouse* CreateTargetDataUnderMouse(UGameplayAbility* OwningAbility);

	UPROPERTY(BlueprintAssignable)
	FMouseTargetDataSignature ValidData;

private:

	virtual void Activate() override;

	//客户端向服务器端提交数据
	void SendMouseCursorData();

	//当数据提交到服务器端后的委托回调
	void OnTargetDataReplicatedCallback(const FGameplayAbilityTargetDataHandle& DataHandle, FGameplayTag ActivationTag);
};

TargetDataUnderMouse.cpp

// 版权归暮志未晚所有。


#include "AbilitySystem/AbilityTasks/TargetDataUnderMouse.h"

#include "AbilitySystemComponent.h"

UTargetDataUnderMouse* UTargetDataUnderMouse::CreateTargetDataUnderMouse(UGameplayAbility* OwningAbility)
{
	UTargetDataUnderMouse* MyObj = NewAbilityTask<UTargetDataUnderMouse>(OwningAbility);
	return MyObj;
}

void UTargetDataUnderMouse::Activate()
{
	//是否由客户端控制
	const bool bIsLocallyControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();
	if(bIsLocallyControlled)
	{
		//如果是客户端控制器控制,实现将数据发射到服务器端
		SendMouseCursorData();
	}
	else
	{
		const FGameplayAbilitySpecHandle SpecHandle = GetAbilitySpecHandle();
		const FPredictionKey ActivationPredictionKey = GetActivationPredictionKey();
		AbilitySystemComponent.Get()->AbilityTargetDataSetDelegate(SpecHandle, ActivationPredictionKey).AddUObject(this, &UTargetDataUnderMouse::OnTargetDataReplicatedCallback);

		//判断在服务器端,上面的委托是否已经广播过
		const bool bCalledDelegate = AbilitySystemComponent.Get()->CallReplicatedTargetDataDelegatesIfSet(SpecHandle, ActivationPredictionKey);
		if(!bCalledDelegate)
		{
			//设置服务器端等待PlayerData数据的上传
			SetWaitingOnRemotePlayerData();
		}
	}
}

void UTargetDataUnderMouse::SendMouseCursorData()
{
	//创建一个预测窗口,该窗口允许客户端在不确定服务器响应的情况下,对游戏状态进行预测性更新。
	FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent.Get(), true);
	
	//获取鼠标拾取结果
	APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
	FHitResult CursorHit;
	PC->GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);

	//创建需要上传服务器端的TargetData
	FGameplayAbilityTargetData_SingleTargetHit* Data = new FGameplayAbilityTargetData_SingleTargetHit();
	Data->HitResult = CursorHit;

	//创建TargetData句柄,上传到服务器端需要上传句柄
	FGameplayAbilityTargetDataHandle DataHandle;
	DataHandle.Add(Data);

	//将TargetData上传至服务器端
	AbilitySystemComponent->ServerSetReplicatedTargetData(
		GetAbilitySpecHandle(),
		GetActivationPredictionKey(),
		DataHandle,
		FGameplayTag(),
		AbilitySystemComponent->ScopedPredictionKey);

	//判断服务器端是否通过验证
	if(ShouldBroadcastAbilityTaskDelegates())
	{
		ValidData.Broadcast(DataHandle);
	}
}

void UTargetDataUnderMouse::OnTargetDataReplicatedCallback(const FGameplayAbilityTargetDataHandle& DataHandle, FGameplayTag ActivationTag)
{
	//通知客户端 服务器端已经接收并处理了从客户端复制的目标数据(将服务器的TargetData应用到客户端,并清除掉缓存)
	AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey());
	//判断服务器端是否通过验证
	if(ShouldBroadcastAbilityTaskDelegates())
	{
		ValidData.Broadcast(DataHandle);
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JetBrains RubyMine v2024.1 安装教程 (Rails/Ruby集成开发环境)

前言 JetBrains RubyMine是一款杰出而全面的Ruby代码编辑器&#xff0c;能够深入识别动态语言的细节&#xff0c;为用户提供智能编码辅助功能。它能够自动检测错误代码段&#xff0c;并提供智能的编写和修复代码的建议&#xff0c;帮助用户进行代码重构。此外&#xff0c;Ruby…

在Redux Toolkit中使用redux-persist进行状态持久化

在 Redux Toolkit 中使用 redux-persist 持久化插件的步骤如下: 安装依赖 npm install redux-persist配置 persistConfig 在 Redux store 配置文件中(例如 store.js)&#xff0c;导入必要的模块并配置持久化选项: import { combineReducers } from redux; import { persist…

Jmeter插件技术:性能测试中服务端资源监控

性能测试过程中我们需要不断的监测服务端资源的使用情况&#xff0c;例如CPU、内存、I/O等。 Jmeter的插件技术可以很好的实时监控到服务器资源的运行情况&#xff0c;并以图形化的方式展示出来&#xff0c;非常方便我们性能测试分析。 操作步骤&#xff1a; 1、安装插件管理…

主打国产算力 广州市通用人工智能公共算力中心项目签约

4月9日&#xff0c;第十届广州国际投资年会期间&#xff0c;企商在线&#xff08;北京&#xff09;数据技术股份有限公司与广州市增城区政府就“广州市通用人工智能公共算力中心”项目进行签约。 该项目由广州市增城区人民政府发起&#xff0c;企商在线承建。项目拟建成中国最…

后端工程师——Java工程师如何准备面试

在国内,Java 程序员是后端开发工程师中最大的一部分群体,其市场需求量也是居高不下,C++ 程序员也是热门岗位之一,此二者的比较也常是热点话题,例如新学者常困惑的问题之一 —— 后端开发学 Java 好还是学 C++ 好。读完本文后,我们可以从自身情况、未来的发展,岗位需求量…

【JVM系列】关于静态块、静态属性、构造块、构造方法的执行顺序

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Java算法 空间换时间(找重复)

一、算法示例 1、题目&#xff1a;题目&#xff1a;0-999的数组中&#xff0c;添加一个重复的元素&#xff0c;打乱后&#xff0c;找出这个重复元素 代码示例&#xff1a; package com.zw.study.algorithm; import java.util.*; public class XorTest {public static void mai…

Vue报错 Cannot read properties of undefined (reading ‘websiteDomains‘) 解决办法

浏览器控制台如下报错&#xff1a; Unchecked runtime.lastError: The message port closed before a response was received. Uncaught (in promise) TypeError: Cannot read properties of undefined (reading websiteDomains) at xl-content.js:1:100558 此问题困扰了…

可持续发展:制造铝制饮料罐要消耗多少资源?

铝制饮料罐是人们经常使用的日常用品&#xff0c;无论是在购物、午休还是在自动售货机前选择喝什么的时候&#xff0c;很少有人会想知道装他们喝的饮料的罐子到底是如何制成的&#xff0c;或者这些铝罐的原材料是如何进出的。 虽然有化学品和一些合金进入铝饮料罐制造过程或成为…

【VSCode调试技巧】Pytorch分布式训练调试

最近遇到个头疼的问题&#xff0c;对于单机多卡的训练脚本&#xff0c;不知道如何使用VSCode进行Debug。 解决方案&#xff1a; 1、找到控制分布式训练的启动脚本&#xff0c;在自己的虚拟环境的/lib/python3.9/site-packages/torch/distributed/launch.py中 2、配置launch.…

【Qt常用控件】—— 输入类控件

目录 1.1 Line Edit 1.2 Text Edit 1.3 Combo Box 1.4 Spin Box 1.5 Date Edit & Time Edit 1.6 Dial 1.7 Slider 1.1 Line Edit QLineEdit是Qt中的一个控件&#xff0c;用于 接收和显示单行文本输入。 核心属性 属性 说明 text 输⼊框中的⽂本 inputMask 输⼊…

Science Robotics 美国斯坦福大学研制了外行星洞穴探测机器人

月球和火星上的悬崖、洞穴和熔岩管已被确定为具有地质和天体生物学研究理想地点。由于其隔绝特性&#xff0c;这些洞穴提供了相对稳定的条件&#xff0c;可以促进矿物质沉淀和微生物生长。在火星上&#xff0c;这些古老的地下环境与火星表面可能适合居住时几乎没有变化&#xf…

JavaEE 初阶篇-深入了解网络通信相关的基本概念(三次握手建立连接、四次挥手断开连接)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 网络通信概述 1.1 基本的通信架构 2.0 网络通信三要素 3.0 网络通信三要素 - IP 地址 3.1 查询 IP 地址 3.2 IP 地址由谁供应&#xff1f; 3.3 IP 域名 3.4 IP 分…

大模型接口管理和分发系统One API

老苏就职于一家专注于音视频实时交互技术和智能算法的创新企业。公司通过提供全面的 SDK 和解决方案&#xff0c;助力用户轻松实现实时音视频通话和消息传递等功能。尽管公司网站上有详细的文档中心&#xff0c;但在实际开发中&#xff0c;仍面临大量咨询工作。 鉴于此&#x…

知识图谱嵌入领域的重要研究:编辑基于语言模型的知识图谱嵌入

今天&#xff0c;向大家介绍一篇在知识图谱嵌入领域具有重要意义的研究论文——Editing Language Model-based Knowledge Graph Embeddings。这项工作由浙江大学和腾讯公司的研究人员联合完成&#xff0c;为我们在动态更新知识图谱嵌入方面提供了新的视角和方法。 研究背景 在…

Linux安装MongoDB超详细

Linux端安装 我们从MonDB官网下载Linux端的安装包&#xff0c;建议下载4.0版本 打开虚拟机&#xff0c;在虚拟机上安装传输工具lrzsz,将下载好的.tgz包拖到虚拟机当中&#xff0c;拖到/usr/local/mongoDB目录下&#xff0c; [rootserver ~]# yum install -y lrzsz [rootser…

如何使用 Vercel 托管静态网站

今天向大家介绍 Vercel 托管静态网站的几种方式&#xff0c;不熟悉 Vercel 的伙伴可以看一下之前的文章&#xff1a;Vercel: 开发者免费的网站托管平台 Github 部署 打开 Vercel 登录界面&#xff0c;推荐使用 GitHub账号 授权登录。 来到控制台界面&#xff0c;点击 Add New …

四川古力未来科技抖音小店:科技新宠,购物新体验

在当下数字化、智能化的时代&#xff0c;电商平台如雨后春笋般涌现&#xff0c;其中不乏一些富有创新精神和实力雄厚的科技企业。四川古力未来科技有限公司就是其中的佼佼者&#xff0c;其抖音小店更是凭借其独特的魅力和优质的服务&#xff0c;赢得了广大消费者的青睐。 一、科…

6步教你APP广告高效变现,收益翻倍秘诀大揭秘!

移动应用广告变现最佳实践与策略指南 在移动应用市场中&#xff0c;广告变现已成为开发者和公司获取收益的重要途径。然而&#xff0c;如何在保证用户体验的同时&#xff0c;实现广告收入的最大化&#xff0c;成为了众多开发者和公司面临的挑战。本文将为您介绍一些最佳的实践…

Seal^_^【送书活动第2期】——《Flink入门与实战》

Seal^_^【送书活动第2期】——《Flink入门与实战》 一、参与方式二、本期推荐图书2.1 作者简介2.2 编辑推荐2.3 前 言2.4 本书特点2.5 内容简介2.6 本书适用读者2.7 书籍目录 三、正版购买 一、参与方式 评论&#xff1a;"掌握Flink&#xff0c;驭大数据&#xff0c;实战…