UEC++ day6

news2024/11/25 12:52:04

简易战斗系统

删除替换父类组件

  • 现在需要添加剑的组件,但是一般来说附着到蒙皮骨骼的东西,也是蒙皮骨骼,所以我们可以新建一个类重新编写也可以直接继承Interoperable类然后不管UStaticMeshComponent这个组件,新建USkeletalMeshComponent再或者直接更改UStaticMeshComponent,因为USkeletalMeshComponent与UStaticMeshComponent都继承自UMeshComponent类,我们把父类Interoperable类中的UStaticMeshComponent换成UMeshComponent类就可以在继承Interoperable的类中修改UStaticMeshComponent为USkeletalMeshComponent。
  • 我们在这里介绍第三种方法
  • 首先在interoperable类中更改UStaticMeshComponent为UMeshComponent。
    在这里插入图片描述
  • 然后在Weapon子类里面销毁DisplayMesh,重新创建DisplayMesh为USkeletalMeshComponent类型,注意这里的TEXT标识换一下名字,否则UE可能会崩溃,然后添加到根组件上
    在这里插入图片描述

添加武器外观与粒子效果

  • 创建Weapon蓝图添加SkeletonMesh与particle即可
    在这里插入图片描述

武器类添加需求

  • 毫无疑问父类的碰撞重叠那两个函数需要基础过来使用,然后定义剑被拾起的声音组件,是否保留被装备后的粒子效果bool变量,以及两个函数用来检测武器是否被拾起
  • Weapon.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GamePlay/InteroperableItem.h"
#include "WeaponItem.generated.h"

/**
 * 
 */
UCLASS()
class UEGAME_API AWeaponItem : public AInteroperableItem
{
	GENERATED_BODY()
public:
	AWeaponItem();

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon|Sound")
	class USoundCue* OnEquipSound;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon|Particle")
	bool bOnEquipParticle;

protected:
	virtual void BeginPlay()override;

public:
	virtual void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)override;
	virtual void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)override;
	
	void Equip();
	void UnEuip();
};
  • Weapon.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "WeaponItem.h"
#include "Components/SkeletalMeshComponent.h"
AWeaponItem::AWeaponItem()
{
	//销毁
	if (DisplayMesh)
	{
		DisplayMesh->DestroyComponent();
		//UE_LOG(LogTemp, Warning, TEXT("delete succeed"));
	}
	else
	{
		//UE_LOG(LogTemp, Warning, TEXT("fail to delete"));
	}
	
	//因为TEXT具有唯一性,我们不知道什么时候销毁原UStaticMeshComponent* DisplayMesh;所以这里TEXT进行一下区分
	DisplayMesh=CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("DisplaySkeletalMesh"));
	DisplayMesh->SetupAttachment(GetRootComponent());
}

void AWeaponItem::BeginPlay()
{

}

void AWeaponItem::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{

}

void AWeaponItem::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{

}

void AWeaponItem::Equip()
{

}

void AWeaponItem::UnEuip()
{

}

给右手添加武器

  • 在人物骨骼里面新建一个socket,然后把武器插入预览调整到合适的大小及位置
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 名字最好不要有英文,因为到时候编码会用到这个插槽
    在这里插入图片描述

拾取武器机制

通知角色当前的靠近的武器

  • 设置一下轴事件映射
    在这里插入图片描述
  • 目的:当玩家走入触发剑的触发器,按F可以拾取这把剑,走到其他剑触发器时,可以交换两把剑
  • 我们需要在MainPlayer类中新建一个bool变量用来检测角色是否拿着武器,新建两个WeaponItem指针来标记当前装备的剑,和是否重叠了别的剑,然后在WeaponItem类中新建一个枚举类型用来标识可拾取与已经拾取状态,新建一个枚举类型变量用来表明状态
  • MainPlayer.h
	//检测是否持剑
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
	bool bIsWeapon;

	//正在装备的武器
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
	class AWeaponItem* EquipWeapon;
	//正在重叠的武器
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
	AWeaponItem* OverlapWeapon;
  • Weapon.h
UENUM(BlueprintType)
enum class EWeaponState :uint8
{
	EWS_CanPickUp UMETA(DisplayName = "CanPickUp"),
	EWS_Equip UMETA(DisplayName = "Equip")
};
	//武器拾取状态
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
	EWeaponState WeaponState;
  • 首先初始化武器是可拾取的状态,然后在开始重叠事件里面判断当前武器是否可拾取然后是不是角色进入触发器范围,如果是就告诉角色的正在重叠的武器是当前武器,在结束重叠事件里面判断,当前角色是否进入触发器范围以及当前的拾取的武器是不是当前武器,如果是就将重叠武器的指针变为空指针。
  • Weapon.cpp
AWeaponItem::AWeaponItem()
{
	//销毁
	if (DisplayMesh)
	{
		DisplayMesh->DestroyComponent();
		//UE_LOG(LogTemp, Warning, TEXT("delete succeed"));
	}
	else
	{
		//UE_LOG(LogTemp, Warning, TEXT("fail to delete"));
	}
	
	//因为TEXT具有唯一性,我们不知道什么时候销毁原UStaticMeshComponent* DisplayMesh;所以这里TEXT进行一下区分
	DisplayMesh=CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("DisplaySkeletalMesh"));
	DisplayMesh->SetupAttachment(GetRootComponent());

	//拾取武器后粒子效果默认关闭
	bOnEquipParticle = false;
	//默认状态武器是可拾取的
	WeaponState = EWeaponState::EWS_CanPickUp;
}

void AWeaponItem::BeginPlay()
{
	Super::BeginPlay();
}

void AWeaponItem::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	Super::OnOverlapBegin(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult);
	
	if (OtherActor && WeaponState == EWeaponState::EWS_CanPickUp)
	{
		AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
		if (Player)
		{
			//告诉角色正在重叠的武器是当前武器
			Player->OverlapWeapon = this;
		}
	}
}

void AWeaponItem::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	Super::OnOverlapEnd(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex);
	
	if (OtherActor)
	{
		AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
		//判断一开始是否拾起的武器是当前武器
		if (Player && Player->OverlapWeapon == this)
		{
			//告诉角色离开了武器触发器
			Player->OverlapWeapon = nullptr;
		}
	}
}

武器与角色的交互事件绑定

  • 新建一个与F事件绑定的函数接口
  • 绑定映射F事件
	PlayerInputComponent->BindAction("Interact", IE_Pressed, this, &AMainPlayer::InteractKeyDown);//按下F
  • 把WeaponItem类中的两个装备卸载函数传入MainPlayer指针类型参数
	void Equip(class AMainPlayer* Player);
	void UnEuip(AMainPlayer* Player);
  • 编写角色拾取武器逻辑
void AMainPlayer::InteractKeyDown()
{
	if (OverlapWeapon)
	{
		if (EquipWeapon)
		{
			//交换武器
			EquipWeapon->UnEuip(this);
			OverlapWeapon->Equip(this);
		}
		else
		{
			//装备武器
			OverlapWeapon->Equip(this);
		}
	}
	else
	{
		if (EquipWeapon)
		{
			//卸载武器
			EquipWeapon->UnEuip(this);
		}
	}
}

装备武器逻辑

  • 在Equip函数里面实现装备武器逻辑,我们首先将枚举状态改为已经装备武器,然后获取到右手的Socket,然后将武器添加上去,将bIsWeapon改为true,EquipWeapon改为true,OverlapWeapon为nullptr,播放音乐与关闭粒子组件
  • 头文件:#include "Engine/SkeletalMeshSocket.h"const USkeletalMeshSocket* RightHandSocker = Player->GetMesh()->GetSocketByName(TEXT("RightHandSocker"));
void AWeaponItem::Equip(AMainPlayer* Player)
{
	if (Player)
	{
		//已装备武器
		WeaponState = EWeaponState::EWS_Equip;
		//获取Player的Socket
		const USkeletalMeshSocket* RightHandSocker = Player->GetMesh()->GetSocketByName(TEXT("RightHandSocker"));
		if (RightHandSocker)
		{
			//让武器附属到Socket上
			RightHandSocker->AttachActor(this, Player->GetMesh());
			Player->bIsWeapon = true;
			Player->EquipWeapon = this;
			Player->OverlapWeapon = nullptr;

			bRotate = false;//武器旋转关闭

			if (OnEquipSound)
			{
				//播放音乐
				UGameplayStatics::PlaySound2D(this, OnEquipSound);
			}
			if (!bOnEquipParticle)
			{
				//关闭粒子组件
				ParticleEffectsComponent->Deactivate();
				
			}
		}
	}
}
  • 回顾一下硬编码声音
  • 头文件 #include "UObject/ConstructorHelpers.h"
	static ConstructorHelpers::FObjectFinder<USoundCue> SoundCueAsset(TEXT("SoundCue'/Game/Assets/Audios/Blade_Cue.Blade_Cue'"));
	if (SoundCueAsset.Succeeded())
	{
		OnEquipSound = SoundCueAsset.Object;
	}

卸载武器逻辑

  • 我们首先需要明白的逻辑,当我在跳跃状态肯定是不能丢弃武器的也不能拾取武器,所以我们在判断时需要加上角色是否跳跃中,然后将其枚举变量武器状态切换成可装备武器状态,主角正在装备武器bool变量为false,装备武器为nullptr,然后我们需要判断武器重叠状态是否为空,如果是就将此时的重叠状态设置为Player自己,最后从Socket中丢弃武器,设置武器的旋转与大小位置
  • DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);:分离当前WeaponItem Socket
void AWeaponItem::Equip(AMainPlayer* Player)
{
	if (Player && !Player->GetMovementComponent()->IsFalling())
	{
		//已装备武器
		WeaponState = EWeaponState::EWS_Equip;
		//获取Player的Socket
		const USkeletalMeshSocket* RightHandSocker = Player->GetMesh()->GetSocketByName(TEXT("RightHandSocket"));
		if (RightHandSocker)
		{
			//让武器附属到Socket上
			RightHandSocker->AttachActor(this, Player->GetMesh());
			Player->bIsWeapon = true;
			Player->EquipWeapon = this;
			Player->OverlapWeapon = nullptr;
			bRotate = false;//武器旋转关闭

			if (OnEquipSound)
			{
				//播放音乐
				UGameplayStatics::PlaySound2D(this, OnEquipSound);
			}
			//if (!bOnEquipParticle)
			//{
			//	//关闭粒子组件
			//	ParticleEffectsComponent->Deactivate();
			//	
			//}
		}
	}
}

void AWeaponItem::UnEuip(AMainPlayer* Player)
{
	if (Player && !Player->GetMovementComponent()->IsFalling())
	{
		WeaponState = EWeaponState::EWS_CanPickUp;
		Player->bIsWeapon = false;
		Player->EquipWeapon = nullptr;
		if (Player->OverlapWeapon == nullptr)
		{
			Player->OverlapWeapon = this;
		}

		//分离当前WeaponItem Socket
		DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
		SetActorRotation(FRotator(0.f));
		SetActorScale3D(FVector(1.f));
		bRotate = true;
	}
}

给武器添加PhysicsAsset与动态切换武器碰撞

  • 我们可以创建三把剑的PhysicsAsset设置碰撞胶囊
    在这里插入图片描述
    在这里插入图片描述
  • 新建两个函数用来激活碰撞与关闭碰撞
  • 注意虚幻4.23后的更新
    • PhysicsOnly:刚体、约束
    • QueryOnly:扫描、重叠、移动
    • QueryAndPhysics:上述全部
	//动态切换碰撞
	void ActiveDisplayMeshCollision();
	void DeactiveDisplayMeshCollision();
	
void AWeaponItem::ActiveDisplayMeshCollision()
{
	DisplayMesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
	DisplayMesh->SetCollisionObjectType(ECollisionChannel::ECC_WorldStatic);
	DisplayMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
	DisplayMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Block);
}

void AWeaponItem::DeactiveDisplayMeshCollision()
{
	DisplayMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

切换持剑移动动画

  • 新建一个BlendSpace1D,添加上动画
    在这里插入图片描述
  • 然后在动画蓝图里面移动的状态机嵌套状态机添加切换持剑移动与不持剑移动
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

蒙太奇插入攻击片段

  • 蒙太奇:可以理解为剪辑,我们可以在状态机里面插入蒙太奇去执行攻击动画
  • 创建蒙太奇,将攻击片段插入好
    在这里插入图片描述

在这里插入图片描述

使用Notifies添加挥剑音效

在这里插入图片描述
在这里插入图片描述

使用Notifies添加拖尾粒子特效

  • 注意这个粒子特效必须要支持拖尾,方可添加,不是任何一种粒子特效都可以添加上的
  • 添加两个插槽用来播放拖尾粒子的位置
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

分析攻击所需逻辑

  • 我们得新建三个变量,一个bool变量用来标识位是否攻击键按下,一个bool变量用来判断是否要攻击在蓝图中方便使用,和蒙太奇的引用变量,然后按键事件得绑定,需要两个函数用来绑定攻击按下与抬起,然后是开始攻击函数与攻击结束函数用来抒写主要攻击动画播放逻辑,攻击结束我们不知道什么时候结束,所以我们要把攻击结束函数添加反射在蓝图中去拿到攻击结束时间
	bool bAttackKeyDown;//是否按下攻击键

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Animation")
	bool bIsAttacking;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Animation")
	class UAnimMontage* AttackMontage;

//-------------------------------------------------------------------------
void AttackKeyDown();

FORCEINLINE void AttackKeyUp() { bAttackKeyDown = false; }

void AttackBegin();
	
UFUNCTION(BlueprintCallable)
void AttackEnd();
	//攻击
	PlayerInputComponent->BindAction("Attack", IE_Pressed, this, &AMainPlayer::AttackKeyDown);
	PlayerInputComponent->BindAction("Attack", IE_Released, this, &AMainPlayer::AttackKeyUp);
	
void AMainPlayer::AttackKeyDown()
{
}


void AMainPlayer::AttackBegin()
{
}

void AMainPlayer::AttackEnd()
{
}

攻击逻辑实现

  • 当我们按下攻击键时,将检测攻击键按下的bool变量为true,安全检测一下是否持剑,如果是就执行攻击函数,攻击函数里面就先判断是否攻击,如果是就将是否攻击先标记为true,然后拿到动画,安全检测动画与蒙太奇,设置播放速率与部分蒙太奇片段的名字,开启蒙太奇与播放指定判断,最后在攻击结束函数里面将是否攻击变量标记为false,判断攻击键是否按下,按下就形成闭环再执行攻击键按下函数。
  • 获取蒙太奇函数的头文件:#include "Animation/AnimInstance.h"
//拿到动画
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();

if (AnimInstance && AttackMontage)
{
	float PlayRate = FMath::RandRange(1.25f, 1.75f);
	FString SectionName = FString::FromInt(FMath::RandRange(1, 2));
			//指定片段播放
	AnimInstance->Montage_Play(AttackMontage, PlayRate);
	AnimInstance->Montage_JumpToSection(FName(*SectionName), AttackMontage);
}
  • 攻击逻辑
void AMainPlayer::AttackKeyDown()
{
	bAttackKeyDown = true;
	if (bIsWeapon)
	{
		AttackBegin();
	}

}


void AMainPlayer::AttackBegin()
{
	if (!bIsAttacking)
	{
		bIsAttacking = true;

		//拿到动画
		UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();

		if (AnimInstance && AttackMontage)
		{
			float PlayRate = FMath::RandRange(1.25f, 1.75f);
			FString SectionName = FString::FromInt(FMath::RandRange(1, 2));
			//指定片段播放
			AnimInstance->Montage_Play(AttackMontage, PlayRate);
			AnimInstance->Montage_JumpToSection(FName(*SectionName), AttackMontage);
		}
	}
}

void AMainPlayer::AttackEnd()
{
	bIsAttacking = false;
	//形成闭环
	if (bAttackKeyDown)
	{
		AttackKeyDown();
	}
}

攻击逻辑蓝图实现

  • 首先在蒙太奇中新建两个结束攻击的通知
    在这里插入图片描述
    在这里插入图片描述

  • 在动画蓝图与角色蓝图中添加蒙太奇
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 正在攻击的时候就不能卸载武器
    在这里插入图片描述
  • 移动中不可攻击
    在这里插入图片描述

MainPlayer.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MainPlayer.generated.h"

//声明移动状态枚举
UENUM(BlueprintType)
enum class EPlayerMovementStatus :uint8
{
	EPMS_Normal UMETA(DisplayName = "Normal"),
	EPMS_Sprinting UMETA(DisplayName = "Sprinting"),
	EPMS_Dead UMETA(DisplayName = "Dead")
};

UENUM(BlueprintType)
enum class EPlayerStaminaStatus :uint8
{
	EPSS_Normal UMETA(DisplayName = "Normal"),
	EPSS_Exhausted UMETA(DisplayName = "Exhausted"),
	EPSS_ExhaustedRecovering UMETA(DisplayName = "ExhaustedRecovering")
};



UCLASS()
class UEGAME_API AMainPlayer : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMainPlayer();

	//新建一个SpringArm
	UPROPERTY(visibleAnywhere,BlueprintReadOnly)
	class USpringArmComponent* SpringArm;
	//新建一个Camera
	UPROPERTY(visibleAnywhere, BlueprintReadOnly)
	class UCameraComponent* FollowCamera;

	
	float BaseTurnRate;		//使用键盘X转向的速率
	float BaseLookUpRate;	//使用键盘Y转向的速率

	//主角状态
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Playe State")
	float Health;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playe State")
	float MaxHealth;
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Playe State")
	float Stamina;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playe State")
	float MaxStamina;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Playe State")
	float StaminaConsumeRate;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Playe State", meta = (ClampMin = 0, ClampMax = 1))
	float ExhaustedStamina;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Playe State")
	int Coins;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Player State")
	float RunningSpeed;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Player State")
	float SprintSpeed;
	UPROPERTY(VisibleAnywhere,BlueprintReadWrite,Category="Player State")
	EPlayerMovementStatus MovementStatus;
	UPROPERTY(VisibleAnywhere,BlueprintReadWrite,Category="Player State")
	EPlayerStaminaStatus StaminaStatus;

	bool bLeftShiftDown;
	
	//检测是否持剑
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
	bool bIsWeapon;

	//正在装备的武器
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
	class AWeaponItem* EquipWeapon;

	//正在重叠的武器
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
	AWeaponItem* OverlapWeapon;

	bool bAttackKeyDown;//是否按下攻击键

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Animation")
	bool bIsAttacking;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Animation")
	class UAnimMontage* AttackMontage;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	//重新Character类中的Jump方法
	void Jump() override;

	void MoveForward(float value);
	void MoveRight(float value);

	void Turn(float Value);
	void LookUp(float Value);

	void TurnRate(float Rate);
	void LookUpRate(float Rate);

	//改变状态
	UFUNCTION(BlueprintCallable,Category="Player|State")
	void AddHealth(float value);
	UFUNCTION(BlueprintCallable, Category = "Player|State")
	void AddStamina(float value);
	UFUNCTION(BlueprintCallable, Category = "Player|State")
	void AddCoin(float value);

	//重写TakeDamage方法
	float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;

	//短小精悍
	FORCEINLINE void LeftShiftDown() { bLeftShiftDown = true; }
	FORCEINLINE void LeftShiftUp() { bLeftShiftDown = false; }

	void SetMovementStatus(EPlayerMovementStatus Status);

	void InteractKeyDown();

	void AttackKeyDown();

	FORCEINLINE void AttackKeyUp() { bAttackKeyDown = false; }

	void AttackBegin();
	
	UFUNCTION(BlueprintCallable)
	void AttackEnd();
};

MainPlayer.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "MainPlayer.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GamePlay/WeaponItem.h"
#include "Animation/AnimInstance.h"
// Sets default values
AMainPlayer::AMainPlayer()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	SpringArm->SetupAttachment(GetRootComponent());
	//设置SPringArm无碰撞臂长
	SpringArm->TargetArmLength = 600.f;
	SpringArm->bUsePawnControlRotation = true;//硬编码SpringArm继承controlller旋转为真


	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->SetupAttachment(SpringArm, NAME_None);
	FollowCamera->bUsePawnControlRotation = false;//硬编码FollowCamera继承controlller旋转为假

	//设置胶囊体的默认宽高
	GetCapsuleComponent()->SetCapsuleSize(35.f, 100.f);

	//对Character的Pawn进行硬编码
	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
	bUseControllerRotationRoll = false;

	//硬编码orient Rotation to Movement,给个默认转向速率
	GetCharacterMovement()->bOrientRotationToMovement = true;
	GetCharacterMovement()->RotationRate = FRotator(0.f, 500.f, 0.f);

	//设置跳跃初始值与在空中的坠落时横向运动控制量
	GetCharacterMovement()->JumpZVelocity = 600.f;
	GetCharacterMovement()->AirControl = 0.15f;

	//给键盘控制转向的速率变量赋初值
	BaseTurnRate = 21.f;
	BaseLookUpRate = 21.f;

	//初始化角色状态
	MaxHealth = 100.f;
	Health = MaxHealth;
	MaxStamina = 200.f;
	Stamina = MaxStamina;
	StaminaConsumeRate = 20.f;
	ExhaustedStamina = 0.167f;
	Coins = 0;
	RunningSpeed = 600.f;
	SprintSpeed = 900.f;
	MovementStatus = EPlayerMovementStatus::EPMS_Normal;
	StaminaStatus = EPlayerStaminaStatus::EPSS_Normal;

	//默认没有按下shift
	bLeftShiftDown = false;


}

// Called when the game starts or when spawned
void AMainPlayer::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void AMainPlayer::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	switch (StaminaStatus)
	{
	case EPlayerStaminaStatus::EPSS_Normal:
		//当Shift按下
		if (bLeftShiftDown)
		{
			if (Stamina - StaminaConsumeRate * DeltaTime <= MaxStamina * ExhaustedStamina)
			{
				StaminaStatus = EPlayerStaminaStatus::EPSS_Exhausted;
			}
			//无论是不是精疲力尽状态都要减去当前帧冲刺消耗的耐力
			Stamina -= StaminaConsumeRate * DeltaTime;
			SetMovementStatus(EPlayerMovementStatus::EPMS_Sprinting);
		}
		else
		{
			//当Shift没有按下,恢复耐力
			Stamina = FMath::Clamp(Stamina + StaminaConsumeRate * DeltaTime, 0.f, MaxStamina);
			SetMovementStatus(EPlayerMovementStatus::EPMS_Normal);
		}
		break;
	case EPlayerStaminaStatus::EPSS_Exhausted:
		if (bLeftShiftDown)
		{
			//如果耐力已经为0
			if (Stamina - StaminaConsumeRate * DeltaTime <= 0.f)
			{
				//么我们需要内部编码把shift抬起,此时StaminaStatus状态转换为ExhaustedRecovering状态,然后设置移动状态为Normal
				LeftShiftUp();
				StaminaStatus = EPlayerStaminaStatus::EPSS_ExhaustedRecovering;	
				SetMovementStatus(EPlayerMovementStatus::EPMS_Normal);
			}
			else
			{
				Stamina -= StaminaConsumeRate * DeltaTime;
			}
		}
		else
		{
			StaminaStatus = EPlayerStaminaStatus::EPSS_ExhaustedRecovering;
			Stamina = FMath::Clamp(Stamina + StaminaConsumeRate * DeltaTime, 0.f, MaxStamina);
			SetMovementStatus(EPlayerMovementStatus::EPMS_Normal);
		}
		break;
	case EPlayerStaminaStatus::EPSS_ExhaustedRecovering:
		//当恢复大于疲劳区时,StaminaStatus状态为Normal
		if (Stamina + StaminaConsumeRate * DeltaTime >= MaxStamina * ExhaustedStamina)
		{
			StaminaStatus = EPlayerStaminaStatus::EPSS_Normal;
		}
		//这状态值肯定是加定了
		Stamina += StaminaConsumeRate * DeltaTime;

		//抬起shift
		LeftShiftUp();
		SetMovementStatus(EPlayerMovementStatus::EPMS_Normal);
		break;
	default:
		break;
	}
}

// Called to bind functionality to input
void AMainPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	//检查PlayerInputComponent指针,check函数只能在这使用
	check(PlayerInputComponent);

	//绑定跳跃轴映射事件
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AMainPlayer::Jump);//按下空格
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);//抬起空格

	PlayerInputComponent->BindAction("Sprint", IE_Pressed, this, &AMainPlayer::LeftShiftDown);//按下shift
	PlayerInputComponent->BindAction("Sprint", IE_Released, this, &AMainPlayer::LeftShiftUp);//抬起shift

	//拾取剑
	PlayerInputComponent->BindAction("Interact", IE_Pressed, this, &AMainPlayer::InteractKeyDown);//按下F

	//攻击
	PlayerInputComponent->BindAction("Attack", IE_Pressed, this, &AMainPlayer::AttackKeyDown);
	PlayerInputComponent->BindAction("Attack", IE_Released, this, &AMainPlayer::AttackKeyUp);

	//绑定移动轴映射事件
	PlayerInputComponent->BindAxis("MoveForward", this, &AMainPlayer::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight", this, &AMainPlayer::MoveRight);

	//绑定Controller控制器去管理视角旋转
	PlayerInputComponent->BindAxis("Turn", this, &AMainPlayer::Turn);
	PlayerInputComponent->BindAxis("LookUp", this, &AMainPlayer::LookUp);

	//绑定键盘鼠标轴映射事件
	PlayerInputComponent->BindAxis("TurnRate", this, &AMainPlayer::TurnRate);
	PlayerInputComponent->BindAxis("LookUpRate", this, &AMainPlayer::LookUpRate);

}

void AMainPlayer::Jump()
{
	//继承父类的方法
	Super::Jump();
}

void AMainPlayer::MoveForward(float value)
{
	if (Controller != nullptr && value != 0.f && !(bIsAttacking))
	{
		//获取到Control旋转
		FRotator Rotation = Controller->GetControlRotation();
		//转向只关注水平Yaw方向,因此置0防止影响
		FRotator YowRotation = FRotator(0.0f, Rotation.Yaw, 0.0f);
		//获取相机(鼠标控制器的朝向),并且朝这个轴的方向移动
		FVector Direction = FRotationMatrix(YowRotation).GetUnitAxis(EAxis::X);
		AddMovementInput(Direction, value);
	}

}

void AMainPlayer::MoveRight(float value)
{
	if (Controller != nullptr && value != 0.f && !(bIsAttacking))
	{
		//获取到Controller旋转
		FRotator Rotation = Controller->GetControlRotation();
		//转向只关注水平Yaw方向,因此置0防止影响
		FRotator YowRotation = FRotator(0.0f, Rotation.Yaw, 0.0f);
		//获取相机(鼠标控制器的朝向),并且朝这个轴的方向移动
		FVector Direction = FRotationMatrix(YowRotation).GetUnitAxis(EAxis::Y);
		AddMovementInput(Direction, value);
	}
}

void AMainPlayer::Turn(float Value)
{
	if (Value != 0.f)
	{
		AddControllerYawInput(Value);
	}
	
}

void AMainPlayer::LookUp(float Value)
{
	//UE_LOG(LogTemp, Warning, TEXT("%f"), GetControlRotation().Pitch);

	//控制视角
	if (GetControlRotation().Pitch < 270.f && GetControlRotation().Pitch >180.f && Value > 0.f)
	{
		return;
	}
	else if (GetControlRotation().Pitch < 180.f && GetControlRotation().Pitch >45.f && Value < 0.f)
	{
		return;
	}
	AddControllerPitchInput(Value);
}

void AMainPlayer::TurnRate(float Rate)
{
	//要乘以一个DeltaTime这样就可以避免高帧底帧差值问题
	float Value = Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds();
	if (Value != 0.f)
	{
		AddControllerYawInput(Value);
	}
}

void AMainPlayer::LookUpRate(float Rate)
{
	//要乘以一个DeltaTime这样就可以避免高帧底帧差值问题
	float Value = Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds();
	//控制视角
	if (GetControlRotation().Pitch < 270.f && GetControlRotation().Pitch >180.f && Value > 0.f)
	{
		return;
	}
	else if (GetControlRotation().Pitch < 180.f && GetControlRotation().Pitch >45.f && Value < 0.f)
	{
		return;
	}
	AddControllerPitchInput(Value);

}

void AMainPlayer::AddHealth(float value)
{
	Health = FMath::Clamp(Health + value, 0.f, MaxHealth);
}

void AMainPlayer::AddStamina(float value)
{
	Stamina = FMath::Clamp(Stamina + value, 0.f, MaxStamina);
}

void AMainPlayer::AddCoin(float value)
{
	Coins += value;
}

float AMainPlayer::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	if (Health - Damage <= 0.f)
	{
		Health = FMath::Clamp(Health - Damage, 0.f, MaxHealth);
		//TODO Die();
	}
	else
	{
		Health -= Damage;
	}
	return Health;
}

void AMainPlayer::SetMovementStatus(EPlayerMovementStatus Status)
{
	MovementStatus = Status;
	//切换状态的时候改变移动速度
	switch (MovementStatus)
	{
	case EPlayerMovementStatus::EPMS_Sprinting:
		GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
		break;
	default:
		GetCharacterMovement()->MaxWalkSpeed = RunningSpeed;
		break;
	}
}

void AMainPlayer::InteractKeyDown()
{
	if (OverlapWeapon)
	{
		if (EquipWeapon)
		{
			//交换武器
			EquipWeapon->UnEuip(this);
			OverlapWeapon->Equip(this);
		}
		else
		{
			//装备武器
			OverlapWeapon->Equip(this);
		}
	}
	else
	{
		if (EquipWeapon)
		{
			//卸载武器
			EquipWeapon->UnEuip(this);
		}
	}
}

void AMainPlayer::AttackKeyDown()
{
	bAttackKeyDown = true;
	if (bIsWeapon)
	{
		AttackBegin();
	}

}


void AMainPlayer::AttackBegin()
{
	if (!bIsAttacking)
	{
		bIsAttacking = true;

		//拿到动画
		UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();

		if (AnimInstance && AttackMontage)
		{
			float PlayRate = FMath::RandRange(1.25f, 1.75f);
			FString SectionName = FString::FromInt(FMath::RandRange(1, 2));
			//指定片段播放
			AnimInstance->Montage_Play(AttackMontage, PlayRate);
			AnimInstance->Montage_JumpToSection(FName(*SectionName), AttackMontage);
		}
	}
}

void AMainPlayer::AttackEnd()
{
	bIsAttacking = false;
	//形成闭环
	if (bAttackKeyDown)
	{
		AttackKeyDown();
	}
}

WeaponItem.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GamePlay/InteroperableItem.h"
#include "WeaponItem.generated.h"

/**
 * 
 */
UENUM(BlueprintType)
enum class EWeaponState :uint8
{
	EWS_CanPickUp UMETA(DisplayName = "CanPickUp"),
	EWS_Equip UMETA(DisplayName = "Equip")
};
UCLASS()
class UEGAME_API AWeaponItem : public AInteroperableItem
{
	GENERATED_BODY()
public:
	AWeaponItem();

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon|Sound")
	class USoundCue* OnEquipSound;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon|Particle")
	bool bOnEquipParticle;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
	EWeaponState WeaponState;

protected:
	virtual void BeginPlay()override;

public:
	virtual void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)override;
	virtual void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)override;
	
	void Equip(class AMainPlayer* Player);
	void UnEuip(AMainPlayer* Player);

	//动态切换碰撞
	void ActiveDisplayMeshCollision();
	void DeactiveDisplayMeshCollision();
};

WeaponItem.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "WeaponItem.h"
#include "Components/SkeletalMeshComponent.h"
#include "Characters/Player/MainPlayer.h"
#include "Engine/SkeletalMeshSocket.h"
#include "Kismet/GameplayStatics.h"
#include "Sound/SoundCue.h"
#include "Particles/ParticleSystemComponent.h"
#include "UObject/ConstructorHelpers.h"
#include "GameFramework/CharacterMovementComponent.h"

AWeaponItem::AWeaponItem()
{
	//销毁
	if (DisplayMesh)
	{
		DisplayMesh->DestroyComponent();
		//UE_LOG(LogTemp, Warning, TEXT("delete succeed"));
	}
	else
	{
		//UE_LOG(LogTemp, Warning, TEXT("fail to delete"));
	}
	
	//因为TEXT具有唯一性,我们不知道什么时候销毁原UStaticMeshComponent* DisplayMesh;所以这里TEXT进行一下区分
	DisplayMesh=CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("DisplaySkeletalMesh"));
	DisplayMesh->SetupAttachment(GetRootComponent());
	ActiveDisplayMeshCollision();//设置碰撞

	static ConstructorHelpers::FObjectFinder<USoundCue> SoundCueAsset(TEXT("SoundCue'/Game/Assets/Audios/Blade_Cue.Blade_Cue'"));
	if (SoundCueAsset.Succeeded())
	{
		OnEquipSound = SoundCueAsset.Object;
	}

	//拾取武器后粒子效果默认关闭
	bOnEquipParticle = false;
	//默认状态武器是可拾取的
	WeaponState = EWeaponState::EWS_CanPickUp;
}

void AWeaponItem::BeginPlay()
{
	Super::BeginPlay();
}

void AWeaponItem::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	Super::OnOverlapBegin(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult);
	
	if (OtherActor && WeaponState == EWeaponState::EWS_CanPickUp)
	{
		AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
		if (Player)
		{
			//告诉角色正在重叠的武器是当前武器
			Player->OverlapWeapon = this;
		}
	}
}

void AWeaponItem::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	Super::OnOverlapEnd(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex);
	
	if (OtherActor)
	{
		AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
		//判断一开始是否拾起的武器是当前武器
		if (Player && Player->OverlapWeapon == this)
		{
			//告诉角色离开了武器触发器
			Player->OverlapWeapon = nullptr;
		}
	}
}

void AWeaponItem::Equip(AMainPlayer* Player)
{
	if (Player && !Player->GetMovementComponent()->IsFalling())
	{
		//已装备武器
		WeaponState = EWeaponState::EWS_Equip;
		DeactiveDisplayMeshCollision();//关闭碰撞
		//获取Player的Socket
		const USkeletalMeshSocket* RightHandSocker = Player->GetMesh()->GetSocketByName(TEXT("RightHandSocket"));
		if (RightHandSocker)
		{
			//让武器附属到Socket上
			RightHandSocker->AttachActor(this, Player->GetMesh());
			Player->bIsWeapon = true;
			Player->EquipWeapon = this;
			Player->OverlapWeapon = nullptr;
			bRotate = false;//武器旋转关闭

			if (OnEquipSound)
			{
				//播放音乐
				UGameplayStatics::PlaySound2D(this, OnEquipSound);
			}
			//if (!bOnEquipParticle)
			//{
			//	//关闭粒子组件
			//	ParticleEffectsComponent->Deactivate();
			//	
			//}
			
		}
	}
}

void AWeaponItem::UnEuip(AMainPlayer* Player)
{
	if (Player && !Player->GetMovementComponent()->IsFalling() && !Player->bIsAttacking)
	{
		WeaponState = EWeaponState::EWS_CanPickUp;
		ActiveDisplayMeshCollision();//开启碰撞
		Player->bIsWeapon = false;
		Player->EquipWeapon = nullptr;
		if (Player->OverlapWeapon == nullptr)
		{
			Player->OverlapWeapon = this;
		}

		//分离当前WeaponItem Socket
		DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
		SetActorRotation(FRotator(0.f));
		SetActorScale3D(FVector(1.f));
		bRotate = true;	
	}
}

void AWeaponItem::ActiveDisplayMeshCollision()
{
	DisplayMesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
	DisplayMesh->SetCollisionObjectType(ECollisionChannel::ECC_WorldStatic);
	DisplayMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
	DisplayMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Block);
}

void AWeaponItem::DeactiveDisplayMeshCollision()
{
	DisplayMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

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

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

相关文章

HarmonyOS开发(四):应用程序入口UIAbility

1、UIAbility概述 UIAbility 一种包含用户界面的应用组件用于与用户进行交互系统调度的单元为应用提供窗口在其中绘制界同 注&#xff1a;每一个UIAbility实例&#xff0c;都对应一个最近任务列表中的任务。 一个应用可以有一个UIAbility也可以有多个UIAbility。 如一般的…

微服务学习|Nacos配置管理:统一配置管理、配置热更新、配置共享、搭建Nacos集群

统一配置管理 在微服务当中&#xff0c;提供一个配置中心来将一些配置提取出来&#xff0c;进行统一的使用&#xff0c;Nacos既可以充当注册中心&#xff0c;也提供配置中心的功能。 1.在Nacos中添加配置文件 在Nacos控制台&#xff0c;我们可以在配置管理中&#xff0c;添加…

golang学习笔记——使用结构

使用结构 有时&#xff0c;你需要在一个结构中表示字段的集合。 例如&#xff0c;要编写工资核算程序时&#xff0c;需要使用员工数据结构。 在 Go 中&#xff0c;可使用结构将可能构成记录的不同字段组合在一起。 Go 中的结构也是一种数据结构&#xff0c;它可包含零个或多个…

盛元广通开放实训室管理系统2.0

开放实训室管理系统是一种基于网络和数据库的实训室信息管理系统&#xff0c;旨在提高实训室的管理水平&#xff0c;实现实训资源的优化配置和高效利用。该系统通常包括用户管理、设备管理、课程管理、考核管理等功能模块&#xff0c;能够实现实训室的预约、设备借用、课程安排…

一周互联网简讯 | 本周互联网发生了啥?(第3期)

1.百度T7跳槽字节3-1&#xff0c;总包145万&#xff0c;压力太大想降级 硕士毕业工作10年&#xff0c;一百度T7大头兵发文称&#xff0c;自己最近拿到字节3-1的offer&#xff0c;年包从现有的110万涨30%到145万。但是担心去字节后因为定的职级高需要带人&#xff0c;压力会很大…

2023年11月界面制作软件合集,新手也能学会!

在今天的互联网时代&#xff0c;有各种界面制作软件可供选择。这些软件可以帮助新手和专业人士创建精美且高效的界面设计。从最基础的拖拽操作到复杂的编程接口&#xff0c;不同的软件提供了一系列的功能和特性&#xff0c;满足了各种需求。我们将在本文中探讨8大神器&#xff…

『亚马逊云科技产品测评』活动征文|构建生态农场家禽系统

『亚马逊云科技产品测评』活动征文&#xff5c;构建生态农场家禽系统 授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 前…

德思特分享丨一文带你了解ADC测试参数有哪些?

来源&#xff1a;德思特测量测试 德思特分享丨一文带你了解ADC测试参数有哪些&#xff1f; 一文带你了解ADC测试参数有哪些 模数转换器&#xff08;ADC&#xff09;是数字电子系统中重要组成部分&#xff0c;用于捕获外部世界的模拟信号&#xff0c;如声音、图像、温度、压力…

SIMULIA|Abaqus 2022x新功能介绍第三弹

Abaqus 线性分析的功能增强 模态分析中增加connector单元的输出 模态线性动力学分析中增加下列Connector单元的输出&#xff0c;无需指定* connector MOTION即可实现&#xff1a;AXIAL&#xff0c;BUSHING&#xff0c;CARDAN&#xff0c;CARTESIAN和ROTATION。 而且改进了CT…

高防CDN有什么作用?

分布式拒绝服务攻击&#xff08;DDoS攻击&#xff09;是一种针对目标系统的恶意网络攻击行为&#xff0c;DDoS攻击经常会导致被攻击者的业务无法正常访问&#xff0c;也就是所谓的拒绝服务。 常见的DDoS攻击包括以下几类&#xff1a; 网络层攻击&#xff1a;比较典型的攻击类…

python中,axis=0,axis=1,axis=2的理解【对于按待定轴求和,axis=‘x’的理解】

一、axis 1.假设我们有一个5行4列的矩阵&#xff0c;见下图所示。 这个矩阵的shape&#xff1a;[5][4],维度是2&#xff0c;axis&#xff1a;0,1&#xff0c;按照行&#xff0c;轴为0&#xff0c;按照列轴为1。 当我们按照axis0&#xff0c;求sum时&#xff1a;我们得到的shap…

网站被攻击怎么办?

网站被大流量攻击会造成服务器资源耗尽&#xff0c;一直到宕机崩溃&#xff0c;网站无法访问甚至被机房停用&#xff0c;时间长就导致网站排名下降&#xff0c;所以必需及时处理。下面跟大家分享服务器被大流量攻击怎么办&#xff1f;服务器攻击防护如何做&#xff1f; 一、服…

win10无损升级到win11

win10无损升级到win11&#xff0c;保留文件和程序。下面就是升级方法。 1&#xff0c;下载win11升级助手 https://download.microsoft.com/download/5/4/c/54c22b82-d0cd-4e34-9a06-b75823a8aede/Windows11InstallationAssistant.exe 2&#xff0c;启动助手开始安装 安装时需…

dgl的cuda版本安装+对应torch的cuda版本安装

优秀教程 1、pytorch 1.8.1 CUDA11.1 对应的DGL版本是0.6.1&#xff08;linux、windows也适用&#xff09; 2、CUDApytorchDGL安装 3、安装dgl-cuda 4、torch 安装备忘录 dgl文件下载 https://anaconda.org/dglteam/repo可以将下面网址中的cu111改成你想要的版本&#xff0…

Zookeeper中的Watch机制的原理?

前言 Zookeeper是一个分布式协调组件&#xff0c;为分布式架构下的多个应用组件提供了顺序访问控制能力。它的数据存储采用了类似于文件系统的树形结构&#xff0c;以节点的方式来管理存储在Zookeeper上的数据 Zookeeper提供了一个Watch机制&#xff0c;可以让客户端感知到Zook…

来聊聊阿里1688 /拼多多API接口接入| 让需求回到产品端

昨儿办公室讨论起了1688。 对&#xff0c;就是阿里搞批发的那个网站。 在上面&#xff0c;你可以买到各种各样价格低廉的产品&#xff0c;比如&#xff0c;办公用具、女孩子的皮筋、小孩子的玩具等等。 在小批量上&#xff0c;它和拼多多定价类似&#xff0c;但二者的赛道却不同…

PDF控件Spire.PDF for .NET【转换】演示:自定义宽度、高度将 PDF 转 SVG

我们在上一篇文章中演示了如何将 PDF 页面转换为 SVG 文件格式。本指南向您展示如何使用最新版本的 Spire.PDF 以及 C# 和 VB.NET 指定输出文件的宽度和高度。 Spire.Doc 是一款专门对 Word 文档进行操作的 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻…

【Mysql】基于MySQL协议的抓包工具

mysql_sniffer 是一个基于 MySQL 协议的抓包工具&#xff0c;用来实时抓取 MySQL 服务端的请求&#xff0c;并格式化输出&#xff0c;输出内容包括访问时间、来源 IP、执行的SQL语句。 在进行MySQL 8.0升级时&#xff0c;了解新版本对SQL语法的改变和新增的功能是非常重要的。通…

解锁安全与信任的双重礼遇!JoySSL证书买二送一,买三送二

JoySSL是业内领先的SSL证书提供商&#xff0c;致力于为网站提供最高水平的安全性。通过使用JoySSL证书&#xff0c;您的网站将获得强大的加密保护&#xff0c;确保用户的敏感信息在传输过程中得到安全加密&#xff0c;有效地抵御各种网络威胁。 为何选择JoySSL证书&#xff1f…

SBPL 打印机上传图片

A计算图片大小 选择图片》打开方式》画图 选择“主页”》重新调整大小 选择“像素” 图片大小计算公式 原图像素/打印机像素&#xff08;每毫米几个点&#xff09;*要求图片的大小&#xff08;mm&#xff09; 例&#xff1a;使用SR224R打印这个logo&#xff0c;大小要求为5m…