UE5.3 C++ 房屋管理系統(一)

一.框架思路

1.如何加載。房屋管理,既然管理。就存在動態加載,和靜態加載的考慮。如果是靜態加載,就是在編輯器情況下放置,但這樣方便了擺放,但管理就需要在開始是將所有的房屋找到加到管理者里。你無法決定拖入場景的類符不符合房屋管理者的要求限制,如果屬性不對,可能會有Bug。動態加載,在運行時,才可以生成設置。這個生成就是一定滿足管理者,不然不生成,相反無論怎么寫都不太可能,寫的運行時和編輯器一樣方便擺放設置。各有優缺點,所以這里我使用的動態加載,原因只是因為動態加載我寫過。

2.采用哪個容器存儲,使用TMap。因為頻繁的訪問,用鍵值隊可以快速找到值也就是房屋類。并且刪除,和修改。不會是大量的數據同時刪除和增加。TMap的建,就使用房屋的ID屬性。保證它的唯一性,和不重復性。

	UPROPERTY(EditAnywhere)TMap<int, ABuildBase*>	m_TargetMap;

3.設計模型,采用單例模式。這里用房屋管理者是單例 UBuildManagerInstance,全局訪問,負責增刪改查。房屋工廠類是單例 UBuildFactory,動態組裝房屋,并設置ID等屬性。

4. 多態,管理不同類型的房屋,這里主要是提高可擴展性,被沒有一定要分很多類型。每個類型,都繼承自房屋基本類ABuildBase。管理TMap里也是存儲 BuildBase。如果有拓展的功能,就用Cast,將對于指向基類指針轉換為,多態的子類。擴展的屬性和功能就能訪問到。

二.框架搭建

1.先把基類和管理者寫好

基類本質繼承自Actor。里面有通用屬性注釋里都有。動態加載 模型后,也是存儲在它自己的,m_childComponentMap組件字典里,這個用在自己讀取JSON文件動態加載,組裝后加入到這個Map。相當于每個房屋,管理自己的身上的模型等部件。

UCLASS()
class LZJCORE_API ABuildBase : public AActor
{GENERATED_BODY()public:	// Sets default values for this actor's propertiesABuildBase();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;
protected:FConfigData ConfigData;  //配置信息//Load model assets in form TEXT("xxxx")class UStaticMesh* GetStaticMeshAssets(const FString MeshName);
public:	UFUNCTION(BlueprintCallable)int GetID();// Called every framevirtual void Tick(float DeltaTime) override;UFUNCTION(BlueprintCallable)virtual void Constitution(const FString& strFile);UFUNCTION(BlueprintCallable)virtual void LoadStaticMesh();UPROPERTY(VisibleAnywhere)TMap<FString, UActorComponent*>	m_childComponentMap;	//組件字典TMap<FString, EComType>	m_childComponentTypeMap;//組件類型字典UPROPERTY(VisibleAnywhere)FString		m_ResourcesPath; //加載模型的路徑UPROPERTY(VisibleAnywhere)FString		m_BuildName;  //房屋對于的名字,加載文件夾UPROPERTY(VisibleAnywhere, BlueprintReadWrite)int m_targetID = 0;//唯一標識//通用//面積//藍圖可訪問通信UPROPERTY(VisibleAnywhere,BlueprintReadWrite)int m_Area = 100;//通用//面積//藍圖可訪問通信UPROPERTY(VisibleAnywhere, BlueprintReadWrite)FString m_Type = "";//居住人口//藍圖可訪問通信UPROPERTY(VisibleAnywhere,BlueprintReadWrite)int m_ResidentPopulation = 100;//價值//藍圖可訪問通信UPROPERTY(VisibleAnywhere, BlueprintReadWrite)int m_Price = 100;//位置//藍圖可訪問通信UPROPERTY(VisibleAnywhere,BlueprintReadWrite)FVector m_Position = FVector::Zero();//方位//藍圖可訪問通信UPROPERTY(VisibleAnywhere, BlueprintReadWrite)FRotator m_Rotation = FRotator::ZeroRotator;// 大小//藍圖可訪問通信UPROPERTY(VisibleAnywhere, BlueprintReadWrite)FVector m_Scale = FVector(1, 1, 1);
};

實現如下,一開始把文件共有路徑設置,Constitution讀取自己對于類型的JSON文件,并進行解析。解析JSON后,獲取配置文件,存到ConfigData結構體里。LoadStaticMesh,加載模型。就是從ConfigData里,讀取數據動態加載,并拼接位置信息,組裝成一個完整房屋。

#include "BuildBase.h"
#include "Components/ChildActorComponent.h"
#include "JsonReadHelper.h"// Sets default values
ABuildBase::ABuildBase()
{// 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;SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent")));
}// Called when the game starts or when spawned
void ABuildBase::BeginPlay()
{Super::BeginPlay();m_ResourcesPath = TEXT("/Script/Engine.StaticMesh'/Game/Models/");
}UStaticMesh* ABuildBase::GetStaticMeshAssets(const FString MeshName)
{///Script/Engine.StaticMesh'/Game/Models/Pipe1/Big.Big'FString tmpPath = m_ResourcesPath + m_BuildName + TEXT("/") + MeshName + TEXT(".") + MeshName + TEXT("'");return LoadObject<UStaticMesh>(nullptr, *tmpPath);
}int ABuildBase::GetID()
{return m_targetID;
}// Called every frame
void ABuildBase::Tick(float DeltaTime)
{Super::Tick(DeltaTime);}void ABuildBase::Constitution(const FString& strFile)
{if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*strFile)){UE_LOG(LogTemp, Error, TEXT("%s not exist"), *strFile);return;}//獲取配置數據m_BuildName = UJsonReadHelper::JsonReader_Line(strFile, TEXT("Name"));ConfigData.StaticMeshInfo = UJsonReadHelper::JsonReader_StaticMesh(strFile);//造房子了LoadStaticMesh();
}void ABuildBase::LoadStaticMesh()
{if (ConfigData.StaticMeshInfo.Num() <= 0)	return;//加載各個組件。。MeshName絕不能重名,否則NewObject和TMap結構都會出問題for (auto& it : ConfigData.StaticMeshInfo){//UClass* baseClass = FindObject<UClass>(ANY_PACKAGE, TEXT("StaticMeshComponent"));UStaticMeshComponent* tmpComp = NewObject<UStaticMeshComponent>(Cast<UObject>(this), *it.NodeID);if (m_childComponentMap.Contains(it.ParentName)){UStaticMeshComponent* tmpParent = Cast<UStaticMeshComponent>(m_childComponentMap[it.ParentName]);if (tmpComp->AttachToComponent(tmpParent, FAttachmentTransformRules::KeepRelativeTransform)){UE_LOG(LogTemp, Warning, TEXT("AttachToComponent"));}}elsetmpComp->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);tmpComp->RegisterComponent();AddInstanceComponent(tmpComp);tmpComp->SetRelativeLocation(it.MeshPos);tmpComp->SetRelativeRotation(it.MeshRot);UStaticMesh* tmpMesh = nullptr;tmpMesh = GetStaticMeshAssets(it.MeshName);tmpComp->SetStaticMesh(tmpMesh);//FVector tmpscale = tmpComp->GetRelativeScale3D();tmpComp->SetRelativeScale3D(it.MeshScale/*FVector(tmpscale.X * (it.ScaleX), tmpscale.Y * (it.ScaleY), tmpscale.Z * (it.ScaleZ))*/);m_childComponentMap.Add(it.NodeID, tmpComp);m_childComponentTypeMap.Add(it.NodeID, EComType::MESH_TYPE);}}

2.其中里面的Constitution函數,是讀取JSON配置文件,利用JSONReaderHelp類。來解析,里面JSONObject,和Reader,FJsonSerializer都是UE自帶的常用的反序列化解析的指針和函數。將JOSN文件里,StaticMesh域下的 各個屬性,解析出來存到結構體數組ConfigData.StaticMeshInfo里。

TArray<FStaticMeshInfo> UJsonReadHelper::JsonReader_StaticMesh(const FString& Path)
{TArray<FStaticMeshInfo> tmpArray;TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(GetFileContentString(Path));TSharedPtr<FJsonObject> Root;if (FJsonSerializer::Deserialize(Reader, Root)){if (Root->HasField(TEXT("StaticMesh"))){const TArray<TSharedPtr<FJsonValue>> Arr = Root->GetArrayField(TEXT("StaticMesh"));for (const auto JsonPtr : Arr) {auto Obj = JsonPtr->AsObject();FStaticMeshInfo tmpStruct;if (Obj->HasField(TEXT("NodeID")))tmpStruct.NodeID = Obj->GetStringField(TEXT("NodeID"));if (Obj->HasField(TEXT("MeshName")))tmpStruct.MeshName = Obj->GetStringField(TEXT("MeshName"));if (Obj->HasField(TEXT("MeshType")))tmpStruct.MeshType = Obj->GetStringField(TEXT("MeshType"));if (Obj->HasField(TEXT("ParentName")))tmpStruct.ParentName = Obj->GetStringField(TEXT("ParentName"));auto smPos = Obj->GetArrayField(TEXT("Position"));tmpStruct.MeshPos.X = smPos[0]->AsNumber();tmpStruct.MeshPos.Y = smPos[1]->AsNumber();tmpStruct.MeshPos.Z = smPos[2]->AsNumber();auto smAtitude = Obj->GetArrayField(TEXT("Rotation"));tmpStruct.MeshRot.Pitch = smAtitude[0]->AsNumber();tmpStruct.MeshRot.Yaw = smAtitude[1]->AsNumber();tmpStruct.MeshRot.Roll = smAtitude[2]->AsNumber();if (Obj->HasField(TEXT("Scale"))){auto scaleVal = Obj->GetArrayField(TEXT("Scale"));tmpStruct.MeshScale.X = scaleVal[0]->AsNumber();tmpStruct.MeshScale.Y = scaleVal[1]->AsNumber();tmpStruct.MeshScale.Z = scaleVal[2]->AsNumber();}tmpArray.Add(tmpStruct);}}}return tmpArray;
}

再從數組里,動態加載網格體模型。相當于加載上面三個模型,拼接他們的相對位置,大小,名字組件,到一個房屋類上。為什么暴露出來再JSON文件里,因為打包后的模型資源你是改不了的。但你可以通過改這個配置文件,對房屋里的組成部分進行微調。大小,位置,旋轉。

void ABuildBase::LoadStaticMesh()
{if (ConfigData.StaticMeshInfo.Num() <= 0)	return;//加載各個組件。。MeshName絕不能重名,否則NewObject和TMap結構都會出問題for (auto& it : ConfigData.StaticMeshInfo){//UClass* baseClass = FindObject<UClass>(ANY_PACKAGE, TEXT("StaticMeshComponent"));UStaticMeshComponent* tmpComp = NewObject<UStaticMeshComponent>(Cast<UObject>(this), *it.NodeID);if (m_childComponentMap.Contains(it.ParentName)){UStaticMeshComponent* tmpParent = Cast<UStaticMeshComponent>(m_childComponentMap[it.ParentName]);if (tmpComp->AttachToComponent(tmpParent, FAttachmentTransformRules::KeepRelativeTransform)){UE_LOG(LogTemp, Warning, TEXT("AttachToComponent"));}}elsetmpComp->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);tmpComp->RegisterComponent();AddInstanceComponent(tmpComp);tmpComp->SetRelativeLocation(it.MeshPos);tmpComp->SetRelativeRotation(it.MeshRot);UStaticMesh* tmpMesh = nullptr;tmpMesh = GetStaticMeshAssets(it.MeshName);tmpComp->SetStaticMesh(tmpMesh);//FVector tmpscale = tmpComp->GetRelativeScale3D();tmpComp->SetRelativeScale3D(it.MeshScale/*FVector(tmpscale.X * (it.ScaleX), tmpscale.Y * (it.ScaleY), tmpscale.Z * (it.ScaleZ))*/);m_childComponentMap.Add(it.NodeID, tmpComp);m_childComponentTypeMap.Add(it.NodeID, EComType::MESH_TYPE);}}

3.房屋管理者,使用UE的UGameInstanceSubsystem的單例方式。這樣自己管理生命周期。

這里就有增刪查,和管理的容器?? TMap<int, ABuildBase*>?? ?m_TargetMap。本質管理者是對容器里的指針指向的在場景里的對象增刪查。改留在UI里寫。

UCLASS()
class LZJCORE_API UBuildManagerInstance : public UGameInstanceSubsystem
{GENERATED_BODY()
public://最早的初始化virtual void Initialize(FSubsystemCollectionBase& Collection);//初始化加載 表里的建筑類void InitialBuilds(TMap<int, FBuildTableStruct> tmp);void setWorld(UWorld* world);void ClearCtrlTarget();public:UFUNCTION()void	AddTarget(int targetID,ABuildBase* target);UFUNCTION()void	RemoveTarget(ABuildBase* target);void	RemoveTarget(int targetID);UFUNCTION()void	RemoveAllTarget();UFUNCTION()bool  IsHasTarget(int targetID);ABuildBase* GetTargetByID(int targetID);UPROPERTY(EditAnywhere)TMap<int, ABuildBase*>	m_TargetMap;UPROPERTY(EditAnywhere)ULZJUserWidget* LZJUserWidget;
protected:UPROPERTY()ABuildBase* m_CtrlTarget;UPROPERTY()UWorld* m_World;
};

實現如下,正常的TMap操作,只是要判斷空指針。這里Initialize初始化,將后續管理的UI界面初始化在主頁界面上。

void UBuildManagerInstance::Initialize(FSubsystemCollectionBase& Collection)
{Super::Initialize(Collection);//!可能加載不了if (UClass* MyWidgetClass = LoadClass<ULZJUserWidget>(NULL, TEXT("/Script/UMGEditor.WidgetBlueprint'/LZJCore/BP_LZJBuildManager.BP_LZJBuildManager_C'"))){if (APlayerController* PC = GetWorld()->GetFirstPlayerController()){LZJUserWidget = CreateWidget<ULZJUserWidget>(PC, MyWidgetClass);if (LZJUserWidget){LZJUserWidget->AddToViewport();}}}
}void UBuildManagerInstance::InitialBuilds(TMap<int, FBuildTableStruct> tmp)
{//for (auto It = tmp.CreateIterator(); It; ++It) {//	int Key = It.Key();   // 獲取ID 鍵//	FBuildTableStruct Value = It.Value(); // 獲取值//	UE_LOG(LogTemp, Warning, TEXT("Key: %d"), Key);//}
}void UBuildManagerInstance::setWorld(UWorld* world)
{m_World = world;}void UBuildManagerInstance::ClearCtrlTarget()
{
}void UBuildManagerInstance::AddTarget(int targetID, ABuildBase* target)  //增
{if (target->IsValidLowLevel()){if(!m_TargetMap.Contains(target->m_targetID)){m_TargetMap.Add(targetID, target);}}
}void UBuildManagerInstance::RemoveTarget(ABuildBase* target)  //刪
{m_TargetMap.Remove(target->GetID());if (target != nullptr){target->Destroy();//m_World->DestroyActor(target);}
}void UBuildManagerInstance::RemoveTarget(int targetID) //刪
{if (m_TargetMap.Contains(targetID)){ABuildBase* ab = *m_TargetMap.Find(targetID);if (ab != nullptr && ab->IsValidLowLevel() && !ab->IsPendingKill()){ab->Destroy();}m_TargetMap.Remove(targetID);}
}void UBuildManagerInstance::RemoveAllTarget()  //全刪
{//m_CtrlTarget = nullptr;for (auto& it : m_TargetMap){ABuildBase* ab = it.Value;if (ab != nullptr){ab->Destroy();}}m_TargetMap.Reset();
}bool UBuildManagerInstance::IsHasTarget(int targetID)
{if (m_TargetMap.Contains(targetID)){return true;}return false;
}ABuildBase* UBuildManagerInstance::GetTargetByID(int targetID) //查 ,改
{if (m_TargetMap.Contains(targetID)){return	*m_TargetMap.Find(targetID);}return nullptr;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79624.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79624.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79624.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

4.1【LLaMA-Factory 實戰】醫療領域大模型:從數據到部署的全流程實踐

【LLaMA-Factory實戰】醫療領域大模型&#xff1a;從數據到部署的全流程實踐 一、引言 在醫療AI領域&#xff0c;構建專業的疾病診斷助手需要解決數據稀缺、知識專業性強、安全合規等多重挑戰。本文基于LLaMA-Factory框架&#xff0c;詳細介紹如何從0到1打造一個垂直領域的醫…

解決LangChain4j報錯HTTP/1.1 header parser received no bytes

問題描述 當使用langchain4j-open-ai調用自己部署的大模型服務時報錯&#xff1a; public static void main(String[] args) {OpenAiChatModel model OpenAiChatModel.builder().apiKey("none").modelName("qwen2.5-instruct").baseUrl("http://19…

阿里云codeup以及本地gitclone+http

cmd命令行亂碼問題、解決 chcp 65001 git代碼提交 git add . git commit -m init git push origin master

2025.05.07-淘天算法崗-第二題

?? 點擊直達筆試專欄 ??《大廠筆試突圍》 ?? 春秋招筆試突圍在線OJ ?? 筆試突圍OJ 02. 完美拼圖挑戰 問題描述 A先生是一位拼圖愛好者,他有兩種形狀的拼圖塊: a a a

Spring Boot中Redis序列化配置詳解

精心整理了最新的面試資料和簡歷模板&#xff0c;有需要的可以自行獲取 點擊前往百度網盤獲取 點擊前往夸克網盤獲取 引言 在使用Spring Boot集成Redis時&#xff0c;序列化方式的選擇直接影響數據存儲的效率和系統兼容性。默認的JDK序列化存在可讀性差、存儲空間大等問題&am…

紫禁城多語言海外投資理財返利源碼帶前端uniapp純工程文件

測試環境&#xff1a;Linux系統CentOS7.6、寶塔、PHP7.2、MySQL5.6&#xff0c;根目錄public&#xff0c;偽靜態thinkphp&#xff0c;開啟ssl證書 語言&#xff1a;中文簡體、英文、越南語、馬來語、日語、巴西語、印尼語、泰語 前端是uniapp的源碼&#xff0c;我已經把nmp給你…

搭建大數據學習的平臺

一、基礎環境準備 1. 硬件配置 物理機&#xff1a;建議 16GB 內存以上&#xff0c;500GB 硬盤&#xff0c;多核 CPU虛擬機&#xff1a;至少 3 臺&#xff08;1 主 2 從&#xff09;&#xff0c;每臺 4GB 內存&#xff0c;50GB 硬盤 2. 操作系統 Ubuntu 20.04 LTS 或 CentOS…

Linux 軟硬連接詳解

目錄 一、軟鏈接&#xff08;Symbolic Link&#xff09; ?定義與特性 ?實現方法?使用 ln -s 命令&#xff1a; 二、硬鏈接&#xff08;Hard Link&#xff09; 1、是什么 2、工作機制 3、實現方式 一、軟鏈接&#xff08;Symbolic Link&#xff09; ?定義與特性 定義…

每日c/c++題 備戰藍橋杯(洛谷P1115 最大子段和)

洛谷P1115 最大子段和 題解 題目描述 最大子段和是一道經典的動態規劃問題。題目要求&#xff1a;給定一個包含n個整數的序列&#xff0c;找出其中和最大的連續子序列&#xff0c;并輸出該最大和。若所有數均為負數&#xff0c;則取最大的那個數。 輸入格式&#xff1a; 第…

前端取經路——框架修行:React與Vue的雙修之路

大家好,我是老十三,一名前端開發工程師。在前端的江湖中,React與Vue如同兩大武林門派,各有千秋。今天,我將帶你進入這兩大框架的奧秘世界,共同探索組件生命周期、狀態管理、性能優化等核心難題的解決之道。無論你是哪派弟子,掌握雙修之術,才能在前端之路上游刃有余。準…

PyTorch API 1 - 概述、數學運算、nn、實用工具、函數、張量

文章目錄 torch張量創建操作索引、切片、連接與變異操作 加速器生成器隨機采樣原地隨機采樣準隨機采樣 序列化并行計算局部禁用梯度計算數學運算常量逐點運算歸約操作比較運算頻譜操作其他操作BLAS 和 LAPACK 運算遍歷操作遍歷操作遍歷操作遍歷操作遍歷操作遍歷操作遍歷操作遍歷…

java命令行打包class為jar并運行

1.創建無包名類: 2.添加依賴jackson 3.引用依賴包 4.命令編譯class文件 生成命令: javac -d out -classpath lib/jackson-core-2.13.3.jar:lib/jackson-annotations-2.13.3.jar:lib/jackson-databind-2.13.3.jar src/UdpServer.java 編譯生成class文件如下 <

ABC 轉 STL 全攻略:格式解析、方法實操與問題解決

在 3D 建模與設計領域&#xff0c;不同格式文件間的轉換是一項基礎且重要的操作。ABC&#xff08;Alembic&#xff09;和 STL&#xff08;Standard Triangle Language&#xff09;是其中常見的兩種格式。ABC 格式因其高效存儲和傳輸 3D 數據的特性&#xff0c;常被用于影視特效…

編寫一個處理txt的loader插件,適用于wbepack

處理txt的webpack的loader插件 編寫一個處理txt的loader插件&#xff0c;適用于wbepack 編寫一個處理txt的loader插件&#xff0c;適用于wbepack 實現一個處理txt的插件&#xff0c;給文本每行前后添加**** module.exports function txtLoader(content) {// 確保 Loader 是異…

DeepSeek的100個應用場景

在春節前夕&#xff0c;浙江杭州的AI企業DeepSeek推出了其開源模型DeepSeek-R1&#xff0c;以僅相當于Open AI最新模型1/30的訓練成本&#xff0c;在數學、編程等關鍵領域展現出媲美GPT-o1的出色性能。發布僅數日&#xff0c;DeepSeek-R1便迅速攀升至中美兩國蘋果應用商店免費榜…

ev_loop_fork函數

libev監視器介紹&#xff1a;libev監視器用法-CSDN博客 libev loop對象介紹&#xff1a;loop對象-CSDN博客 libev ev_loop_fork函數介紹:ev_loop_fork函數-CSDN博客 libev API吐血整理&#xff1a;https://download.csdn.net/download/qq_39466755/90794251?spm1001.2014.3…

【PostgreSQL數據分析實戰:從數據清洗到可視化全流程】金融風控分析案例-10.1 風險數據清洗與特征工程

&#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 文章大綱 PostgreSQL金融風控分析案例&#xff1a;風險數據清洗與特征工程實戰一、案例背景&#xff1a;金融風控數據處理需求二、風險數據清洗實戰&#xff08;一&#xff09;缺失值…

OpenCV 的 CUDA 模塊中用于將一個多通道 GpuMat 圖像拆分成多個單通道圖像的函數split()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 cv::cuda::split 是 OpenCV CUDA 模塊中的一個函數&#xff0c;用于將一個多通道的 GpuMat 圖像拆分成多個單通道的 GpuMat 圖像。這個函數是 CP…

【WebRTC-13】是在哪,什么時候,創建編解碼器?

Android-RTC系列軟重啟&#xff0c;改變以往細讀源代碼的方式 改為 帶上實際問題分析代碼。增加實用性&#xff0c;方便形成肌肉記憶。同時不分種類、不分難易程度&#xff0c;在線征集問題切入點。 問題&#xff1a;編解碼器的關鍵實體類是什么&#xff1f;在哪里&什么時候…

c語言第一個小游戲:貪吃蛇小游戲03

我們為貪吃蛇的節點設置為一個結構體&#xff0c;構成貪吃蛇的身子的話我們使用鏈表&#xff0c;鏈表的每一個節點是一個結構體 顯示貪吃蛇身子的一個節點 我們這邊node就表示一個蛇的身體 就是一小節 輸出結果如下 顯示貪吃蛇完整身子 效果如下 代碼實現 這個hasSnakeNode(…