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

news2024/12/26 11:56:28

在前面的文章中,我们实现了一个火球术的一些基本功能,火球术技能的释放,在技能释放后,播放释放动画,在动画播放到需要释放火球术的位置时,将触发动画通知,在动画通知中触发标签事件,然后再技能中监听事件完成火球术的创建。接下来,我们将继续优化火球术技能,并研究点新的东西。
在上一篇文章中,我们使用了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);
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

课时100:正则表达式_基础实践_基础知识

3.1.1 基础知识 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习 基础知识 需求 我们之前的一些操作&#xff0c;很大程度上都是基于特定的关键字来进行实践的&#xff0c;尤其是面对一些灵活的场景&#xff0c;我们因为过于限定一些关键字&am…

线性代数基础2矩阵

矩阵是什么 矩阵就是二维数组&#xff0c;下面是一个 m 乘 n 的矩阵&#xff0c;它有 m 行&#xff0c;n 列&#xff0c;每行每列上面都有元素&#xff0c;每个元素都有行标i 和列标 j&#xff0c; a ij 。简称m n矩阵&#xff0c;记作&#xff1a; 注意a11的索引是 A[0,0]。…

多模态视觉语言模型:BLIP和BLIP2

1. BLIP BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation BLIP的总体结构如下所示&#xff0c;主要包括三部分&#xff1a; 单模态编码器&#xff08;Image encoder/Text encoder&#xff09;&#xff1a;分别进…

论文笔记:UrbanGPT: Spatio-Temporal Large Language Models

1 intro 时空预测的目标是预测并洞察城市环境随时间和空间不断变化的动态。其目的是预见城市生活多个方面的未来模式、趋势和事件&#xff0c;包括交通、人口流动和犯罪率。虽然已有许多努力致力于开发神经网络技术&#xff0c;以准确预测时空数据&#xff0c;但重要的是要注意…

卷王问卷考试系统/SurveyKing调查系统源码

SurveyKing是一个功能强大的开源调查问卷和考试系统&#xff0c;它能够快速部署并适用于各个行业。 这个系统提供了在线表单设计、数据收集、统计和分析等功能&#xff0c;支持20多种题型&#xff0c;提供多种创建问卷的方式和设置。 项 目 地 址 &#xff1a; runruncode.c…

[阅读笔记16][Orca-2]Teaching Small Language Models How to Reason

接下来是Orca-2&#xff0c;这篇是微软在23年11月发表的论文&#xff0c;在Orca-1的基础上又进行了一些改进。 作者希望教会Orca-2各种推理策略&#xff0c;例如逐步思考、回忆然后回答、先回忆再推理再回答、直接生成回答等等策略。并且Orca-2应该能针对不同任务应该使用最合适…

安装Zipkin

官网&#xff1a;https://zipkin.io/pages/quickstart.html Jar包方式 下载 方式一&#xff1a;百度网盘下载 链接&#xff1a;https://pan.baidu.com/s/1PRV1RamJ8IWX32IJb7jw3Q?pwde8vu 提取码&#xff1a;e8vu 方式二&#xff1a;Central Repository: io/zipkin/zipk…

linux离线安装mysql

一、下载mysql 地址&#xff1a;MySQL 这里选择64为还是32为要根据操作系统来 uname -m 二、上传解压配置mysql 使用root账户登录linux服务器&#xff0c;在opt文件下创建mysql文件夹 cd /opt sudo mkdir mysql 使用Xftp上传mysql压缩包到此文件夹下(自行决定路径) cd mysql/…

李宏毅2022机器学习/深度学习 个人笔记(2)

本系列用于推导、记录该系列视频中本人不熟悉、或认为有价值的知识点 本篇记录第一讲&#xff08;选修&#xff09;&#xff1a;神奇宝贝分类&#xff08;续&#xff09; 如图&#xff0c;boundary变为直线&#xff0c;结果也有上升 我们不一定采用高斯几率模型&#xff0c;…

【C++初识继承】

博主首页&#xff1a; 有趣的中国人 专栏首页&#xff1a; C进阶 本篇文章主要讲解 继承 的相关内容 目录 1. 继承的概念和定义 1.1 继承的概念 1.2 继承的定义 1.2.1 继承定义格式 1.2.2 继承方式与访问修饰限定符 2. 基类和派生类对象赋值转换 3. 继承中的作用域 …

NIMAX下载安装使用,pyvisa基本使用

NIMAX部分&#xff1a; 1、先在NI官网下载系统配置和NI-VISA&#xff1a; 系统配置&#xff1a; https://www.ni.com/zh-cn/support/downloads/drivers/download.system-configuration.html#532687https://www.ni.com/zh-cn/support/downloads/drivers/download.system-conf…

机器学习基本流程

Jupyter Notebook 代码连接&#xff1a; machine_learning_demo machine_learning_ensembles Step 1: Imports and Configuration import pandas as pd import numpy as np import copy import json import pickle import joblib import lightgbm as lgb import optuna impor…

IDEA插件:CodeGeex

前言 CodeGeeX是由清华大学和智谱AI联合开发的多语言代码生成模型。CodeGeeX是一款AI编程助手&#xff0c;其功能类似于Github Copilot、Codeium、CodeWhisperer、Bito等智能编程助手。CodeGeeX支持Python、C、Java、JavaScript、Go等10多种主流编程语言。它可以帮助程…

【小程序】IOS wx小程序解压获取源文件

根据自己手机的系统&#xff0c;获取wx小程序的缓存目录 一、微信小程序文件存放路径 安卓&#xff1a; /data/data/com.tencent.mm/MicroMsg/{{user哈希值}}/appbrand/pkg/iOS越狱&#xff1a; /User/Containers/Data/Application/{{系统UUID}}/Library/WechatPrivate/{{user…

unity学习(89)——unity塞满c盘!--删除editor下的log文件

卸了一个视频后强制续命打开详细信息&#xff1a; 这个再往下找也是没用的&#xff01; 显示隐藏文件夹后&#xff01;执行如下操作&#xff01; 30个g&#xff01; 其中unity占23g editer占了21g 删除C:\Users\王栋林\AppData\Local\Unity\Editor下的log文件 恢复到之前的水…

建筑楼宇VR火灾扑灭救援虚拟仿真软件厂家

在传统消防安全教育方式中&#xff0c;往往存在内容枯燥、参与度低和风险大等问题&#xff0c;使得消防安全知识难以深入人心。然而&#xff0c;借助VR消防安全逃生教育系统&#xff0c;我们可以打破这一困境&#xff0c;为公众带来前所未有的学习体验。 VR消防安全逃生教育系统…

【后端】Thymeleaf模板引擎学习笔记

文章目录 1. java体系模板引擎介绍2. 使用2.1 初步使用 视频地址 1. java体系模板引擎介绍 FreeMarkerThymeleafVelocity 2. 使用 2.1 初步使用 引入依赖 <dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf</artifactId><…

SpringBoot学习之Kafka下载安装和启动(三十三)

一、Mac环境 1、下载Kafka&#xff1a;Apache Kafka 2、这里我选择的版本是kafka_2.12-3.7.0&#xff0c;下载最新版的Kafka二进制文件&#xff0c;解压到你喜欢的目录&#xff08;建议目录不要带中文&#xff09;。 3、启动ZooKeeper服务&#xff0c;Kafka需要使用ZooKeeper&…

Claude 3 Opus 效果是否真的可以超过GPT-4?

实测,不仅是超过,而且我个人感觉这个差距甚至大于GPT3.5到GPT4的距离. claude3在长篇理学论文的解析能力是非常显著的,可以扩展补完作者省略的大量运用高等数学,复变函数以及更多数理方法的计算过程,并且将中间过程补完的非常完美.不会漏符号,错符号,偏差数值之类的问题.工科许…

【网站项目】党员之家服务系统小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…