UE5.1.1 C++从0开始(16.作业5思路分享)

news2024/9/21 12:28:33

教程的链接:https://www.bilibili.com/video/BV1nU4y1X7iQ

总结一下这次的任务点:

  1. 用PlayerState来做一个Credit系统,需要在我们的ui内显示我们的分数
  2. 更新血药对象,每次使用血药都会扣除相应的分数
  3. 新增一个金币对象,每次和一个金币对象交互就会增加一定的分数
  4. 在地图内随机生成金币和血药
  5. 金币和血药需要从同一个父类中继承下来

整个分数系统和我们的血量其实差不多,无非就是增加减少,检查剩余的分数够不够我们用来和血药交互的。还是老规矩,直接上代码

SPlayerState.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "Delegates/DelegateCombinations.h"
#include "SPlayerState.generated.h"

//蓝图宏
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCreditChange, float, NewCredit);


UCLASS()
class ACTIONROGUELIKE_API ASPlayerState : public APlayerState
{
	GENERATED_BODY()

	ASPlayerState();

    //蓝图节点
	UPROPERTY(BlueprintAssignable)
	FOnCreditChange OnCreditChange;

private:
	float Credit;
    
private:
	float CreditMax;

public:
	UFUNCTION(BlueprintCallable,Category="Credit")
	const float GetCredit() {
		return Credit;
	}

public:
	UFUNCTION(BlueprintCallable, Category = "Credit")
	void AddCredit(float DeltaCredit);

	
};

SPlayerState.cpp

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


#include "SPlayerState.h"

ASPlayerState::ASPlayerState()
{
	CreditMax = 100;
	
	Credit = 0;
}

void ASPlayerState::AddCredit(float DeltaCredit)
{
    //我给分数加了个上下限,这个功能可加可不加
	Credit = FMath::Clamp(Credit + DeltaCredit, 0, CreditMax);
	
    //每次更改都会在日志里头打印出来
	UE_LOG(LogTemp, Warning, TEXT("Credit:%f"),Credit);
	
    //蓝图节点广播
	OnCreditChange.Broadcast(Credit);
}



各位应该看得出来,我创建的这个类只有得分这一项,其实看APlayerState.h里头你也能看到一个和分数一样的东西,就是Score。这个类在这里的作用单纯是存储我们的Credit用的,然后提供get set方法而已。同时我也在代码内写了一个广播,那么我在蓝图编辑器内是这样写的。

请添加图片描述

然后就是我们的父类设计,我这里把它命名为SBaseCreditUseActor,因为我能找到的共同点就这点了。在这个类里面我使用了Interface,也就是说我们需要对着这些物品摁e才会触发事件。

SBaseCreditUseActor.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SGameplayInterface.h"
#include "SPlayerState.h"
#include "SBaseCreditUseActor.generated.h"

UCLASS()
class ACTIONROGUELIKE_API ASBaseCreditUseActor : public AActor , public ISGameplayInterface
{
	GENERATED_BODY()
	
    //接口实现
	void Interact_Implementation(APawn* InstigatorPawn) override;
	
	UPROPERTY(EditAnywhere,Category="Component")
	class UStaticMeshComponent* MeshComp;

public:
    //用来判断能否执行实现函数
	UFUNCTION(BlueprintCallable,Category="Credit")
	bool CanImplement(ASPlayerState* ASP);

    //实现函数
	UFUNCTION(BlueprintCallable,Category="Implementation")
	virtual void Implementation(ASPlayerState* ASP , APawn* InstigatorPawn);

public:
    //CreditNeed的get set方法
	UFUNCTION()
	void SetCreditNeed(float Creditneed);

	const float GetCreditNeed() {
		return CreditNeed;
	}

private:
	float CreditNeed;

public:	
	// Sets default values for this actor's properties
	ASBaseCreditUseActor();

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

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


};

SBaseCreditUseActor.cpp

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


#include "SBaseCreditUseActor.h"

//接口实现函数
void ASBaseCreditUseActor::Interact_Implementation(APawn* InstigatorPawn)
{
	if (!CanImplement(InstigatorPawn->GetPlayerState<ASPlayerState>()))
	{
		return;
	}
	Implementation(InstigatorPawn->GetPlayerState<ASPlayerState>(),InstigatorPawn);
}

//判断能否执行实现函数的函数
bool ASBaseCreditUseActor::CanImplement(ASPlayerState*ASP)
{
	return ASP->GetCredit() >= CreditNeed;
}

//实现函数,这里我没写是因为这里的函数每个子类都不相同,所以我们这里的函数都会在子类里面重写
void ASBaseCreditUseActor::Implementation(ASPlayerState* ASP, APawn* InstigatorPawn)
{

}
//set方法
void ASBaseCreditUseActor::SetCreditNeed(float Creditneed)
{
	CreditNeed = Creditneed;
}

// Sets default values
ASBaseCreditUseActor::ASBaseCreditUseActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh Component"));
	RootComponent = MeshComp;

    //CreditNeed默认值
	CreditNeed = 25;

}

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

// Called every frame
void ASBaseCreditUseActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}


这一串代码其实就是我在之前的血瓶的代码的基础上抽象加上一点优化得到的代码,能适应大部分的情况,不过少数的子类仍然需要重写大量的父类函数。

首先是稍微简单一点的金币

SCoin.h

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

#pragma once

#include "CoreMinimal.h"
#include "SBaseCreditUseActor.h"
#include "SCoin.generated.h"

/**
 * 
 */
UCLASS()
class ACTIONROGUELIKE_API ASCoin : public ASBaseCreditUseActor
{
	GENERATED_BODY()

	ASCoin();

	virtual void Implementation(ASPlayerState* ASP, APawn* InstigatorPawn);
};

SCoin.cpp

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


#include "SCoin.h"

ASCoin::ASCoin()
{
	SetCreditNeed(0.0f);
}

void ASCoin::Implementation(ASPlayerState* ASP, APawn* InstigatorPawn)
{
	ASP->AddCredit(10);
	GetWorld()->DestroyActor(this);
}

这个类基本上没有做太多的改动

在之后就是改动比较大的血瓶类

SHealthPotion.h

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

#pragma once

#include "CoreMinimal.h"
#include "SBaseCreditUseActor.h"
#include "SHealthPotion.generated.h"

/**
 * 
 */
UCLASS()
class ACTIONROGUELIKE_API ASHealthPotion : public ASBaseCreditUseActor
{
	GENERATED_BODY()

	void Interact_Implementation(APawn* InstigatorPawn) override;

	virtual void Implementation(ASPlayerState* ASP, APawn* InstigatorPawn) override;

	UPROPERTY(EditDefaultsOnly,Category="Health")
	float HealthDelta;

	ASHealthPotion();
};

SHealthPotion.cpp

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


#include "SHealthPotion.h"
#include "SAttributeComponent.h"


void ASHealthPotion::Interact_Implementation(APawn* InstigatorPawn)
{
	USAttributeComponent* AttributeComp = USAttributeComponent::GetArrtibutes(InstigatorPawn);

    //主要改动是if的判断,我需要除了credit的扣除检测之外还要检测玩家血量,不能说满血也能扣除分数进行补血
	if (!CanImplement(InstigatorPawn->GetPlayerState<ASPlayerState>())||AttributeComp->GetHealth()==100)
	{
		return;
	}
	Implementation(InstigatorPawn->GetPlayerState<ASPlayerState>(), InstigatorPawn);
}

void ASHealthPotion::Implementation(ASPlayerState* ASP, APawn* InstigatorPawn)
{
	USAttributeComponent* AttributeComp = USAttributeComponent::GetArrtibutes(InstigatorPawn);
	AttributeComp->ApplyHealthChange(this, HealthDelta);
	ASP->AddCredit(-GetCreditNeed());
	GetWorld()->DestroyActor(this);
}

ASHealthPotion::ASHealthPotion()
{
	SetCreditNeed(25);
	HealthDelta = 20.0f;
}

写完以上这些之后,就剩下随机生成的问题了,还记得之前的机器人生成的那个函数吗?我们用相同的方法来做。

SGameModeBase.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "EnvironmentQuery/EnvQueryTypes.h"
#include "../../../../../UE_5.1/Engine/Source/Runtime/AIModule/Classes/EnvironmentQuery/EnvQueryInstanceBlueprintWrapper.h"
#include "SGameModeBase.generated.h"

/**
 * 
 */
UCLASS()
class ACTIONROGUELIKE_API ASGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

protected:

	UPROPERTY(EditDefaultsOnly,Category="AI")
	class UEnvQuery* SpawnBotQuery;

	UPROPERTY(EditDefaultsOnly, Category = "AI")
	class UEnvQuery* SpawnHealthQuery;

	UPROPERTY(EditDefaultsOnly,Category="AI")
	TSubclassOf<AActor> MinionClass;

	UPROPERTY(EditDefaultsOnly, Category = "AI")
	TSubclassOf<AActor> HealthClass;

	UPROPERTY(EditDefaultsOnly,Category="AI")
	class UCurveFloat* DifficultyCurve;

	FTimerHandle TimerHandle_SpawnBots;

	UPROPERTY(EditDefaultsOnly,Category = "AI")
	float SpawnTimerInterval;

	UFUNCTION()
	void SpawnBotTimerElapsed();

	UFUNCTION()
	void SpawnHealth();

	UFUNCTION()
	void OnQueryCompleted(class UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus);

	UFUNCTION()
	void OnQueryEnd(class UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus);

public:

	ASGameModeBase();

public:
	virtual void StartPlay() override;

	UFUNCTION(Exec)
	void KillAll();

	UFUNCTION()
	void RespawnPlayerElapsed(AController* Controller);

public:
	virtual void OnActorKilled(AActor* VictimActor, AActor* Killer);
	
};

SGameModeBase.cpp

// Fill out your copyright notice in the Description page of Project Settings.
#include "SGameModeBase.h"
#include "EnvironmentQuery/EnvQueryManager.h"
#include "EnvironmentQuery/EnvQueryTypes.h"
#include "EnvironmentQuery/EnvQueryInstanceBlueprintWrapper.h"
#include "../Public/AI/SAICharacter.h"
#include "../Public/SAttributeComponent.h"
#include "../../../../../UE_5.1/Engine/Source/Runtime/Engine/Public/EngineUtils.h"
#include "SCharacter.h"
#include "SPlayerState.h"
#include "SHealthPotion.h"

static TAutoConsoleVariable<bool> CVarSpawnBots(TEXT("su.SpawnBots"),true,TEXT("Enable Spawing Bots via timer."), ECVF_Cheat);

void ASGameModeBase::SpawnBotTimerElapsed()
{
    //看到下面这个函数没,就是我们插入的函数(其实是我偷懒,懒得再写一个timer了)
	SpawnHealth();
	if (!CVarSpawnBots.GetValueOnGameThread())
	{
		return;
	}

	int32 NrOfAliveBots = 0;
	for (TActorIterator<ASAICharacter>It(GetWorld()); It; ++It)
	{
		ASAICharacter* Bot = *It;

		USAttributeComponent* AttributeComp = USAttributeComponent::GetArrtibutes(Bot);
		if (ensure(AttributeComp) && AttributeComp->IsAlive())
		{
			NrOfAliveBots++;
		}
	}

	float MaxBotCount = 10.0f;

	if (DifficultyCurve)
	{
		MaxBotCount = DifficultyCurve->GetFloatValue(GetWorld()->TimeSeconds);
	}

	if (NrOfAliveBots >= MaxBotCount)
	{
		return;
	}

	UEnvQueryInstanceBlueprintWrapper* QuetyInstance = UEnvQueryManager::RunEQSQuery(this,SpawnBotQuery,this,EEnvQueryRunMode::RandomBest5Pct,nullptr);

	if (ensure(QuetyInstance))
	{
		FScriptDelegate QueryCompleted;
		QueryCompleted.BindUFunction(this, STATIC_FUNCTION_FNAME(TEXT("&ASGameModeBase::OnQueryCompleted")));

		QuetyInstance->GetOnQueryFinishedEvent().Add(QueryCompleted);
	}

	
}

void ASGameModeBase::SpawnHealth()
{
	int32 HealthExist = 0;
	for (TActorIterator<ASHealthPotion>It(GetWorld()); It; ++It)
	{
		HealthExist++;
	}
	float MaxHealth = 5;
	if (MaxHealth<=HealthExist)
	{
		return;
	}
	
	UEnvQueryInstanceBlueprintWrapper* QueryInstance = UEnvQueryManager::RunEQSQuery(this, SpawnHealthQuery, this, EEnvQueryRunMode::RandomBest25Pct, nullptr);

	if (ensure(QueryInstance))
		{
		FScriptDelegate QueryStop;
		QueryStop.BindUFunction(this, STATIC_FUNCTION_FNAME(TEXT("&ASGameModeBase::OnQueryEnd")));
		QueryInstance->GetOnQueryFinishedEvent().Add(QueryStop);
	}
	
	
}

void ASGameModeBase::OnQueryCompleted(UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus)
{
	if (QueryStatus != EEnvQueryStatus::Success)
	{
		UE_LOG(LogTemp,Warning,TEXT("Spawn EQS Failed!!!"))
		return;
	}


	TArray<FVector> Locations = QueryInstance->GetResultsAsLocations();

	if (Locations.IsValidIndex(0))
	{
		GetWorld()->SpawnActor<AActor>(MinionClass,Locations[0],FRotator::ZeroRotator);
	}
}

void ASGameModeBase::OnQueryEnd(class UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus)
{
	if (QueryStatus != EEnvQueryStatus::Success)
	{
		UE_LOG(LogTemp, Warning, TEXT("Spawn EQS Failed!!!"))
			return;
	}
	TArray<FVector> Locations = QueryInstance->GetResultsAsLocations();

	if (Locations.IsValidIndex(0))
	{
		GetWorld()->SpawnActor<AActor>(HealthClass, Locations[0], FRotator::ZeroRotator);
	}
}

ASGameModeBase::ASGameModeBase()
{
	SpawnTimerInterval = 2.0f;
}

void ASGameModeBase::StartPlay()
{
	Super::StartPlay();

	GetWorldTimerManager().SetTimer(TimerHandle_SpawnBots, this, &ASGameModeBase::SpawnBotTimerElapsed, SpawnTimerInterval, true);
}

void ASGameModeBase::KillAll()
{
	for (TActorIterator<ASAICharacter>It(GetWorld()); It; ++It)
	{
		ASAICharacter* Bot = *It;

		USAttributeComponent* AttributeComp = USAttributeComponent::GetArrtibutes(Bot);
		if (ensure(AttributeComp) && AttributeComp->IsAlive())
		{
			AttributeComp->Kill(this);
		}
	}
}

void ASGameModeBase::RespawnPlayerElapsed(AController* Controller)
{
	if (ensure(Controller))
	{
		Controller->UnPossess();

		RestartPlayer(Controller);

		//重生会扣50点Credit
		ASPlayerState* ASP = Cast<ASPlayerState>(Controller->PlayerState);
		if (ASP)
		{
			ASP->AddCredit(-50.0f);
		}
	}
}

//玩家被杀后复活的逻辑
void ASGameModeBase::OnActorKilled(AActor* VictimActor, AActor* Killer)
{
	ASCharacter* Player = Cast<ASCharacter>(VictimActor);
	if (Player)
	{
		FTimerHandle TimerHandle_RespawnDelay;

		FTimerDelegate Delegate;

		Delegate.BindUFunction(this,"RespawnPlayerElapsed",Player->GetController());

		float RespawnDelay = 2.0f;
		GetWorldTimerManager().SetTimer(TimerHandle_RespawnDelay,Delegate, RespawnDelay,false);
	}
}

仿照之前写的那个机器人生成的方法,同时我们把生成的函数直接插入到我们timer结束就会执行的那个函数里头(这是个偷懒的方法),有点投机取巧不过很方便。

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

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

相关文章

【Python】Sphinx 文档生成器

目录 1. Sphinx 介绍 2. Sphinx 实战 2.1. 初始化 Sphinx 工程 2.2. 编译项目 2.3. Sphinx 主题 2.4. 增加 Sphinx 文档 1. Sphinx 介绍 Sphinx是一个Python文档生成器&#xff0c;它基于reStructuredText标记语言&#xff0c;可自动根据项目生成HTML,PDF等格式的文档。…

使用 OpenCV 进行按位运算和图像屏蔽

在本教程中,我们将了解如何使用按位运算 AND、OR、XOR 和 NOT。 图像处理中使用按位运算从图像中提取感兴趣区域 (ROI)。 正如您所看到的,两个矩形重叠的区域已被删除(黑色),因为在该区域中两个像素都大于 0。 按位非<

浅析代谢组学最常用到的数据分析方法 图形详解pca pls-da opls-da

代谢组学是一门对某一生物或细胞所有低分子质量代谢产物&#xff08;以相对分子质量<1000的有机和无机的代谢物为研究核心区&#xff09;进行分析的新兴学科。生物样本通过NMR、GC-MS、LC-MS等高通量仪器分析检测后&#xff0c;能产生大量的数据&#xff0c;这些数据具有高维…

网页版在线流程图绘制工具Diagram

网页地址&#xff1a;Diagram 可以将流程图保存为图片、网址等多种格式。 界面&#xff1a;

【PortAudio】PortAudio 音频处理库Demo

1. 介绍 PortAudio是一个免费、跨平台、开源的音频I/O库。看到I/O可能就想到了文件&#xff0c;但是PortAudio操作的I/O不是文件&#xff0c;而是音频设备。它能够简化C/C的音频程序的设计实现&#xff0c;能够运行在Windows、Macintosh OS X和UNIX之上&#xff08;Linux的各种…

SAP从入门到放弃系列之生产车间相关单据打印

文章目录概览 一、前言二、系统相关设置2.1、配置:1&#xff1a;2.2、配置点2&#xff1a;2.3、配置点3 三、主数据准备四、测试场景准备五、小结 一、前言 通常在项目实施的时候&#xff0c;如果没有MES&#xff0c;那么生产调度相关岗位下达订单后&#xff08;订单下达感觉没…

K8s部署微服务(springboot+vue)

文章目录 前言一、使用到的K8s资源1.1 Deployment1.2 Service 二、Springboot基础服务部署2.1 网关gateway2.2 鉴权auth2.3 文件file2.4 流程flow2.5 消息message2.6 组织org2.7 系统通用system2.8 用户user2.9 Node 三、Vue前端部署3.1 项目前端nginx3.2 静态资源服务nginx 四…

迪杰斯特拉算法(求最短路径)

迪杰斯特拉算法&#xff08;求最短路径&#xff09; 迪杰斯特拉算法用于查找图中某个顶点到其它所有顶点的最短路径&#xff0c;该算法既适用于无向加权图&#xff0c;也适用于有向加权图。 注意&#xff0c;使用迪杰斯特拉算法查找最短路径时&#xff0c;必须保证图中所有边…

相对位置编码(二) Relative Positional Encodings - Transformer-XL

1. Motivation 在Transformer-XL中&#xff0c;由于设计了segments&#xff0c;如果仍采用transformer模型中的绝对位置编码的话&#xff0c;将不能区分处不同segments内同样相对位置的词的先后顺序。 比如对于segmenti&#xfffd;&#xfffd;&#xfffd;&#xfffd;&…

pycharm安装opencv-python报错

问题一 通过pycharm中的Terminal窗口安装opencv-python错误如下&#xff1a; 上图所示为部分错误&#xff0c;全部错误如下&#xff1a; Building wheel for opencv-contrib-python (PEP 517) ... errorERROR: Complete output from command D:\anzhuanglujing\Anaconda\python…

从零开始之PID控制

从零开始系列之PID控制&#xff0c;宗旨就是以说人话的方式讲述它&#xff0c;真正的做到从零开始&#xff0c;小白一看就会&#xff0c;一学就废。 一、什么是PID控制&#xff1f; PID控制&#xff08;比例-积分-微分控制&#xff09;由比例单元&#xff08;Proportional&…

玩耍的猫咪【 InsCode Stable Diffusion 美图活动一期】

1️⃣ 工具介绍 InsCode是一个集成了在线IDE、在线AI编程、在线算力租赁、在线项目部署以及在线SD 模型使用的综合代码开发平台。 Stable Diffusion是目前最火的AI绘画工具之一&#xff0c;它是一个免费开源的项目。通过Stable Diffusion&#xff0c;可以很轻松的通过文字描述…

上半年结束,下半年继续冲!

前言: 这周直播也把雷神写的Ffmpeg推流器讲解完了&#xff0c;而一同时&#xff0c;一转眼间&#xff0c;2023年已经过半&#xff0c;正式进入了下半年&#xff1a; 因为上半年已经开始在做解析Ffmpeg 最新版本的源码&#xff0c;所以下半年&#xff0c;我会继续坚持讲解Ffmpeg…

“GPT+健康医疗”赋能医疗行业“数智化”发展,景联文科技提供高质量医疗数据库

近日&#xff0c;ChatGPT这个代表着通用版的大型语言模型以其出色的表现在全球互联网上引人注目。它所使用的GPT技术基础为人工智能应用开启了全新的世界。 “大模型时代已经到来。它已变成基础设施&#xff0c;变成算力&#xff0c;变成生产力。大模型可能有通用技术&#xf…

C++杂谈-友元和操作符重载

1、友元- friend 我的理解&#xff1a;通过设置友元函数和友元类来让外部函数来访问私有成员&#xff0c;这样虽然破坏了类的封装型和隐藏性&#xff0c;但是提高了程序的运行效率&#xff08;减少了某些安全性检查的过程&#xff09;。 友元函数和友元类统称友元&#xff0c;…

Nginx+Tomcat(多实例)实现动静分离和负载均衡(四层、七层)

目录 一、Tomcat 多实例部署 二、反向代理的两种类型 三、NginxTomcat实现负载均衡和动静分离&#xff08;七层代理&#xff09; 1.动静分离和负载均衡原理 2.实现方法 3.部署实例 &#xff08;1&#xff09;部署Nginx负载均衡服务器 &#xff08;2&#xff09;配置Tom…

C++之GNU C的__attribute__常用属性(一百五十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

使用 ZBrush、Ornatrix 和 Substance 3D Painter 重现哈利波特中的凤凰

今天瑞云渲染小编给大家带来了Ramn Tapia 分享 Phoenix 项目背后的工作流程&#xff0c;解释了如何在 Ornatrix 中完成修饰&#xff0c;并展示了纹理化过程。 介绍 你好&#xff0c;有创造力的读者朋友们 我的名字是Ramn&#xff0c;但在数字艺术领域&#xff0c;我的名字是ra…

【 Android11 无线热点开发 】无线AP开与关、无线AP信息获取

前言 前面四篇文章介绍完了有线网络、无线网络的开发过程&#xff0c;下面介绍下Android 11上网络的终结篇&#xff0c;无线热点的开发流程。 相关文章 1、【 Android11 WiFi开发 一 】WiFi列表获取与展示 2、【 Android11 WiFi开发 二 】WiFi连接、断开 3、【 Android11 Wi…

软件为什么要进行故障演练?主要为了什么?

随着现代社会的高度信息化和软件的广泛应用&#xff0c;软件的质量和可靠性对于保障用户体验和信息安全显得尤为重要。为了保证软件的稳定运行和即时响应&#xff0c;软件故障演练成为软件开发和运维过程中的重要环节&#xff0c;那软件为什么要进行故障演练&#xff1f;主要为…