commit c1c6c7fe7f1c953ba3bfb03989b424124c52c476 Author: Lenheart <947330670@qq.com> Date: Mon Sep 15 11:28:54 2025 +0800 1111 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e81beb4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +MyGame.nro +MyGame.nacp +MyGame.lst +MyGame.elf +build/ +romfs/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..831f1ad --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,28 @@ +{ + "configurations": [ + { + "name": "Switch", + "includePath": [ + "${workspaceFolder}/source/**", + "${workspaceFolder}/source_game/**", + "L:/Switch/devkitPro/devkitA64/aarch64-none-elf/include", + "L:/Switch/devkitPro/libnx/include", + "L:/Switch/devkitPro/portlibs/switch/include", + "L:/Switch/devkitPro/portlibs/switch/include/SDL2" + ], + "defines": [ + "__SWITCH__" + ], + "compilerPath": "L:/Switch/devkitPro/devkitA64/bin/aarch64-none-elf-gcc", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "configurationProvider": "ms-vscode.makefile-tools", + "compilerArgs": [ + "-std=c++17", + "-fno-rtti" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1f20f35 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,115 @@ +{ + "files.associations": { + "*.nut": "squirrel", + "*.cpp": "cpp", + "switch.h": "c", + "squirrel.hpp": "c", + "sdl.h": "c", + "socket.h": "c", + "requestscript.hpp": "c", + "functional": "c", + "locale.h": "c", + "string.h": "c", + "sqdbg.h": "c", + "sstream": "cpp", + "string_view": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "netfwd": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "ranges": "cpp", + "span": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "text_encoding": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp", + "list": "cpp", + "set": "cpp", + "unordered_set": "cpp", + "stdlib.h": "c", + "json.hpp": "c", + "stdio.h": "c", + "strstream": "cpp", + "barrier": "cpp", + "bitset": "cpp", + "cfenv": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "coroutine": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cuchar": "cpp", + "expected": "cpp", + "regex": "cpp", + "source_location": "cpp", + "flat_map": "cpp", + "flat_set": "cpp", + "fstream": "cpp", + "future": "cpp", + "generator": "cpp", + "latch": "cpp", + "mutex": "cpp", + "print": "cpp", + "scoped_allocator": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "spanstream": "cpp", + "stacktrace": "cpp", + "stdfloat": "cpp", + "stop_token": "cpp", + "syncstream": "cpp", + "thread": "cpp", + "typeindex": "cpp" + }, + "Codegeex.RepoIndex": true +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7f82a95 --- /dev/null +++ b/Makefile @@ -0,0 +1,229 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) +# +# NO_ICON: if set to anything, do not use icon. +# NO_NACP: if set to anything, no .nacp file is generated. +# APP_TITLE is the name of the app stored in the .nacp file (Optional) +# APP_AUTHOR is the author of the app stored in the .nacp file (Optional) +# APP_VERSION is the version of the app stored in the .nacp file (Optional) +# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) +# ICON is the filename of the icon (.jpg), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .jpg +# - icon.jpg +# - /default_icon.jpg +# +# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .json +# - config.json +# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead +# of a homebrew executable (.nro). This is intended to be used for sysmodules. +# NACP building is skipped as well. +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +# SOURCES := source source/Tool source/squirrel source/EngineCore source/EngineFrame/Scene source/EngineFrame/Actor source/EngineFrame/Component source_game/Scene source_game/Global source_game/Asset +SOURCES := $(shell find . -type f \( -name "*.c" -o -name "*.cpp" -o -name "*.s" \) -exec dirname {} \; | sort | uniq) +DATA := data +INCLUDES := include source source_game +#ROMFS := romfs + +APP_TITLE := MyProjectDemo +APP_AUTHOR := Lenheart + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := `$(PREFIX)pkg-config --cflags sdl2 SDL2_mixer SDL2_image` -Wall -O2 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -std=c++17 -fno-rtti +# -fno-exceptions + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := `curl-config --libs` `$(PREFIX)pkg-config --libs sdl2 SDL2_mixer SDL2_image SDL2_ttf` \ + -lnx -lsquirrel_static -lsqstdlib_static -lstdc++ -lm + + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.jpg) + ifneq (,$(findstring $(TARGET).jpg,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).jpg + else + ifneq (,$(findstring icon.jpg,$(icons))) + export APP_ICON := $(TOPDIR)/icon.jpg + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_ICON)),) + export NROFLAGS += --icon=$(APP_ICON) +endif + +ifeq ($(strip $(NO_NACP)),) + export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp +endif + +ifneq ($(APP_TITLEID),) + export NACPFLAGS += --titleid=$(APP_TITLEID) +endif + +ifneq ($(ROMFS),) + export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... +ifeq ($(strip $(APP_JSON)),) + @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf +else + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf +endif + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +ifeq ($(strip $(APP_JSON)),) + +all : $(OUTPUT).nro + +ifeq ($(strip $(NO_NACP)),) +$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp +else +$(OUTPUT).nro : $(OUTPUT).elf +endif + +else + +all : $(OUTPUT).nsp + +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm + +$(OUTPUT).nso : $(OUTPUT).elf + +endif + +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/Script/Core/Eunm.nut b/Script/Core/Eunm.nut new file mode 100644 index 0000000..8358827 --- /dev/null +++ b/Script/Core/Eunm.nut @@ -0,0 +1 @@ +print("松鼠脚本系统加载成功!"); \ No newline at end of file diff --git a/Script/Core/UiRender.nut b/Script/Core/UiRender.nut new file mode 100644 index 0000000..3b1e9da --- /dev/null +++ b/Script/Core/UiRender.nut @@ -0,0 +1,32 @@ +/* +文件名:UiRender.nut +路径:Core/UiRender.nut +创建日期:2025-09-07 19:01 +文件用途: +*/ +currentAngle <- 0; + + +function UI_Render(dt) { + + + // // print("ui渲染"); + + // SDL.DrawImg("sprite/interface2/nowloading/nowloading.img", 1, 0, 0); + // SDL.DrawImg("sprite/interface2/nowloading/nowloading.img", 0, 0, 720 - 34); + + // if(!getroottable().rawin("Img1")) Img1 <- SDL.CreateTexture("sprite/interface2/nowloading/nowloading.img", 1); + // if(!getroottable().rawin("Img2")) Img2 <- SDL.CreateTexture("sprite/interface2/nowloading/nowloading.img", 0); + + // SDL.DrawImg(Img1, 0, 0, 1280, 720); + // SDL.DrawImg(Img2, 0, 720 - 34, 1280, 34); + + + // local Speed = 180.0; + // currentAngle += Speed * dt; + // currentAngle = currentAngle % 360.0; + // // print(dt); + // SDL.DrawImgEx("sprite/interface2/nowloading/nowloading.img", 4, 617, 600, 2, currentAngle); + + +} \ No newline at end of file diff --git a/Script/Script.json b/Script/Script.json new file mode 100644 index 0000000..3d60a06 --- /dev/null +++ b/Script/Script.json @@ -0,0 +1,7 @@ +{ + "Files": [ + "Core/Eunm.nut", + "Core/UiRender.nut", + "main.nut" + ] +} \ No newline at end of file diff --git a/Script/chat-0.0.1-SNAPSHOT.jar b/Script/chat-0.0.1-SNAPSHOT.jar new file mode 100644 index 0000000..add4915 Binary files /dev/null and b/Script/chat-0.0.1-SNAPSHOT.jar differ diff --git a/Script/folder-alias.json b/Script/folder-alias.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/Script/folder-alias.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Script/main.nut b/Script/main.nut new file mode 100644 index 0000000..49a56ec --- /dev/null +++ b/Script/main.nut @@ -0,0 +1,5 @@ +function main() { + + + print("松鼠脚本 Main 函数执行!"); +} \ No newline at end of file diff --git a/application.elf b/application.elf new file mode 100644 index 0000000..005b3e4 Binary files /dev/null and b/application.elf differ diff --git a/application.nacp b/application.nacp new file mode 100644 index 0000000..e573dcf Binary files /dev/null and b/application.nacp differ diff --git a/folder-alias.json b/folder-alias.json new file mode 100644 index 0000000..16d83ce --- /dev/null +++ b/folder-alias.json @@ -0,0 +1,80 @@ +{ + "source/EngineCore": { + "description": "引擎核心类" + }, + "source/EngineCore/Scene.h": { + "description": "场景类" + }, + "source/EngineCore/Game.h": { + "description": "游戏主类" + }, + "source/EngineCore/Scene_Base.h": { + "description": "场景基类" + }, + "source/EngineFrame": { + "description": "引擎框架类" + }, + "source/EngineCore/IntrusiveList.hpp": { + "description": "侵入式链表" + }, + "source/Tool/Tool_Network.h": { + "description": "网络请求工具" + }, + "source/Tool/RemoteLogger.h": { + "description": "日志系统" + }, + "source/squirrel/SquirrelEx.h": { + "description": "松鼠系统" + }, + "source/EngineFrame/Actor": { + "description": "演员系统" + }, + "source/EngineFrame/Component": { + "description": "组件系统" + }, + "source/EngineFrame/Scene": { + "description": "场景系统" + }, + "source/Tool/RefPtr.h": { + "description": "引用计数智能指针" + }, + "source/Tool/RefObject.h": { + "description": "引用计数对象" + }, + "source/Tool/Allocator.h": { + "description": "内存分配器" + }, + "source/Tool/Common.h": { + "description": "通用对象类型" + }, + "source/Tool/RefBasePtr.hpp": { + "description": "引用计数基础指针" + }, + "source/Tool/Ifstream_NPK.h": { + "description": "自定义读取流" + }, + "source_game/Scene": { + "description": "游戏场景" + }, + "source_game/Actor/Actor_UI.h": { + "description": "ui角色" + }, + "source_game/Global": { + "description": "全局相关" + }, + "source_game/Scene/Scene_Loading_UI.h": { + "description": "加载界面UI" + }, + "source/EngineFrame/Component/Text.h": { + "description": "文字" + }, + "source/EngineFrame/Component/Sprite.h": { + "description": "图像" + }, + "source_game/Asset": { + "description": "资源" + }, + "source_game/Asset/AnimationStruct.h": { + "description": "Ani结构数据" + } +} \ No newline at end of file diff --git a/source/EngineCore/Asset_ImagePack.cpp b/source/EngineCore/Asset_ImagePack.cpp new file mode 100644 index 0000000..a3b952a --- /dev/null +++ b/source/EngineCore/Asset_ImagePack.cpp @@ -0,0 +1,246 @@ +#include "Asset_ImagePack.h" + +Asset_ImagePack::Asset_ImagePack() +{ +} +Asset_ImagePack::~Asset_ImagePack() +{ +} + +void Asset_ImagePack::Init() +{ + DIR *dir; + struct dirent *ent; + std::string path = "ImagePacks2/"; + dir = opendir(path.c_str()); + + while ((ent = readdir(dir))) + { + // 跳过.和..目录 + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) + continue; + std::string RealPath = path + ent->d_name; + Ifstream_NPK Fs; + Fs.open(RealPath.c_str(), std::ios::in | std::ios::binary); + if (Fs.is_open()) + { + std::string Header = Fs.ReadString(); + // 如果是NPK + if (Header.find("NeoplePack_Bill") != std::string::npos) + { + // 读取img数量 + int ImageCount = Fs.ReadInt(); + // 读取头 + NpkInfo *ImgList = new NpkInfo[ImageCount]; + for (int i = 0; i < ImageCount; i++) + { + ImgList[i].Offset = Fs.ReadInt(); + ImgList[i].Length = Fs.ReadInt(); + ImgList[i].Path = Fs.ReadInfo(); + } + for (int i = 0; i < ImageCount; i++) + { + IMG img; + img.imgOffset = ImgList[i].Offset; + img.imgSize = ImgList[i].Length; + img.img_index = i; + img.lpBelongsFile = ent->d_name; + img.lpImgName = ImgList[i].Path; + img.lp_lplist = NULL; + img.png_sum = 0; + + map_npk.insert(make_pair(img.lpImgName, img)); + } + // 销毁 + delete[] ImgList; + } + } + Fs.close(); + } + closedir(dir); +} + +void Asset_ImagePack::ParseColor(BYTE *Tab, int Type, BYTE *SaveByte, int Offset) +{ + BYTE a = 0; + BYTE r = 0; + BYTE g = 0; + BYTE b = 0; + switch (Type) + { + case 0x0e: + a = (BYTE)(Tab[1] >> 7); + r = (BYTE)((Tab[1] >> 2) & 0x1f); + g = (BYTE)((Tab[0] >> 5) | ((Tab[1] & 3) << 3)); + b = (BYTE)(Tab[0] & 0x1f); + a = (BYTE)(a * 0xff); + r = (BYTE)((r << 3) | (r >> 2)); + g = (BYTE)((g << 3) | (g >> 2)); + b = (BYTE)((b << 3) | (b >> 2)); + break; + case 0x0f: + a = (BYTE)(Tab[1] & 0xf0); + r = (BYTE)((Tab[1] & 0xf) << 4); + g = (BYTE)(Tab[0] & 0xf0); + b = (BYTE)((Tab[0] & 0xf) << 4); + break; + } + SaveByte[Offset + 0] = b; + SaveByte[Offset + 1] = g; + SaveByte[Offset + 2] = r; + SaveByte[Offset + 3] = a; +} + +void Asset_ImagePack::LoadImgToMem(IMG *p) +{ + std::string Path = "ImagePacks2/" + p->lpBelongsFile; + // SDL_Log("LoadImgToMem : %s", Path.c_str()); + Ifstream_NPK Fs; + Fs.open(Path.c_str(), std::ios::in | std::ios::binary); + if (Fs.is_open()) + { + Fs.seekg(p->imgOffset); + std::string Flag = Fs.ReadString(); // 读取Flag + if (Flag.find("Neople Img File") != std::string::npos) + { + // 索引表大小 + long TableLength = Fs.ReadLong(); + // img 版本 4字节 + Fs.ReadInt(); // 读取版本 + // img 帧数 + int IndexCount = Fs.ReadInt(); + // 图片数量赋值 + p->png_sum = IndexCount; + + // new出 Png数量的 结构体 + ImgInfo *PngList = new ImgInfo[IndexCount]; + + for (int i = 0; i < IndexCount; i++) + { + PngList[i].Type = Fs.ReadInt(); + if (PngList[i].Type == 17) + { + // 引用贴图 + int frbuf = Fs.ReadInt(); + // 压缩类型 用来临时存一下引用编号 + PngList[i].CmpType = frbuf; + ////大小 + PngList[i].Size = 0; + PngList[i].Offset = PngList[i - 1].Offset + PngList[i - 1].Size; + continue; + } + // 压缩类型 + PngList[i].CmpType = Fs.ReadInt(); + // 宽度 + PngList[i].Width = Fs.ReadInt(); + // 高度 + PngList[i].Height = Fs.ReadInt(); + // 大小 + PngList[i].Size = Fs.ReadInt(); + // Xpos + PngList[i].Xpos = Fs.ReadInt(); + // Ypos + PngList[i].Ypos = Fs.ReadInt(); + // 帧域X + PngList[i].FrameXpos = Fs.ReadInt(); + // 帧域Y + PngList[i].FrameYpos = Fs.ReadInt(); + // 计算偏移 + if (i == 0) + PngList[i].Offset = 0 + p->imgOffset + TableLength + 32; + else + PngList[i].Offset = PngList[i - 1].Offset + PngList[i - 1].Size; + } + + for (int i = 0; i < IndexCount; i++) + { + // 引用 + if (PngList[i].Type == 17) + { + // 引用编号 + int YYIndex = PngList[i].CmpType; + int sizebuf = PngList[YYIndex].Width * PngList[YYIndex].Height * 4; + BYTE *bByte = new BYTE[sizebuf]; + memcpy(bByte, PngList[PngList[i].CmpType].PNGdata, sizebuf); + PngList[i].PNGdata = PngList[YYIndex].PNGdata; + + // 压缩类型 用来临时存一下引用编号 + PngList[i].CmpType = PngList[YYIndex].CmpType; + // 宽度 + PngList[i].Width = PngList[YYIndex].Width; + // 高度 + PngList[i].Height = PngList[YYIndex].Height; + // 大小 + PngList[i].Size = 0; + // Xpos + PngList[i].Xpos = PngList[YYIndex].Xpos; + // Ypos + PngList[i].Ypos = PngList[YYIndex].Ypos; + // 帧域X + PngList[i].FrameXpos = PngList[YYIndex].FrameXpos; + // 帧域Y + PngList[i].FrameYpos = PngList[YYIndex].FrameYpos; + continue; + } + + Fs.seekg(PngList[i].Offset); + BYTE *PngData = Fs.ReadCustomSize(PngList[i].Size); + int DeSize = PngList[i].Width * PngList[i].Height * 4; + BYTE *bByte = new BYTE[DeSize]; + unsigned long RealSize = DeSize; + uncompress(bByte, &RealSize, PngData, (unsigned long)PngList[i].Size); + delete[] PngData; + + if (PngList[i].Type != 16) + { + int PngByteSize = DeSize * 2; + PngList[i].PNGdata = new BYTE[PngByteSize]; + + for (int e = 0; e < PngByteSize; e += 4) + { + BYTE NeedData[2]; + memset(NeedData, 0, 2); + memcpy(NeedData, bByte + (e / 4) * 2, 2); + ParseColor(NeedData, PngList[i].Type, PngList[i].PNGdata, e); + } + delete[] bByte; + } + else + { + PngList[i].PNGdata = bByte; + } + } + + p->lp_lplist = PngList; + } + else if (Flag.find("Neople Image File") != std::string::npos) + { + // LoadImgToMem2(p); + } + Fs.close(); + } +} + +Asset_ImagePack::IMG *Asset_ImagePack::GetIMG(std::string imgName) +{ + IMG *img = NULL; + std::map::iterator itr; + itr = map_npk.find(imgName); + if (itr == map_npk.end()) + { + std::string mes = "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!调用了不存在的Img : " + imgName; + SDL_Log(mes.c_str()); + img = &map_npk["sprite/interface/base.img"]; + } + else + { + img = &itr->second; + } + // 如果图片数组不存在 或者 图片数据不存在都要重读 + if (!img->lp_lplist) + { + LoadImgToMem(img); + } + img->UseTime = SDL_GetTicks(); + return img; +} diff --git a/source/EngineCore/Asset_ImagePack.h b/source/EngineCore/Asset_ImagePack.h new file mode 100644 index 0000000..e048968 --- /dev/null +++ b/source/EngineCore/Asset_ImagePack.h @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include "Tool/Ifstream_NPK.h" +#include + +class Asset_ImagePack +{ +public: + struct NpkInfo + { + int Offset; + int Length; + std::string Path; + }; + + // PNG结构体 + struct ImgInfo + { + // 图片格式 + int Type; + // 压缩类型 + int CmpType; + // 宽度 + int Width; + // 高度 + int Height; + // 大小 + int Size; + // Xpos + int Xpos; + // Ypos + int Ypos; + // Xpos + int XposEx = 0; + // Ypos + int YposEx = 0; + // 帧域X + int FrameXpos; + // 帧域Y + int FrameYpos; + // 偏移 + int Offset; + // Png位图数据 + BYTE *PNGdata; + }; + + struct IMG // npk的img的结构体 + { + std::string lpImgName; // img文件的路径 + int img_index; // img文件在npk文件里的序号 + unsigned imgOffset; + unsigned imgSize; + std::string lpBelongsFile; // 这个img属于哪个npk文件 + int png_sum; // 这个img文件有多少个 图片 + ImgInfo *lp_lplist; // 图片的数组.. + Uint32 UseTime = 0; + }; + +public: + Asset_ImagePack(const Asset_ImagePack &) = delete; + Asset_ImagePack &operator=(const Asset_ImagePack &) = delete; + Asset_ImagePack(Asset_ImagePack &&) = delete; + Asset_ImagePack &operator=(Asset_ImagePack &&) = delete; + // 全局访问点 + static Asset_ImagePack &GetInstance() + { + static Asset_ImagePack instance; // 局部静态变量,保证只初始化一次 + return instance; + } + +public: + void Init(); + void ParseColor(BYTE *Tab, int Type, BYTE *SaveByte, int Offset); + void LoadImgToMem(IMG *p); + IMG *GetIMG(std::string imgName); + +private: + Asset_ImagePack(/* args */); + ~Asset_ImagePack(); + + std::map map_npk; +}; diff --git a/source/EngineCore/Asset_Script.cpp b/source/EngineCore/Asset_Script.cpp new file mode 100644 index 0000000..bf004fc --- /dev/null +++ b/source/EngineCore/Asset_Script.cpp @@ -0,0 +1,213 @@ +#include "EngineCore/Asset_Script.h" +#include "Asset_Script.h" + +void Asset_Script::Init() +{ + // 读取头建立树 + InitHeader(); + // 读取bin文件 + InitBin(); + // 读取LoadString + InitLoadString(); + + InitFlag = true; +} + +void Asset_Script::Clear() +{ + FileInfo.clear(); + BinStringM.clear(); + LoadStringM.clear(); + _Data.clear(); + _Data.shrink_to_fit(); + InitFlag = false; +} + +void Asset_Script::InitHeader() +{ + // 读取UUID的长度 + int UUID_LENGTH = GetInt(); + // UUID 读 1 - 36位 构造 UTF8 string + std::string UUID = GetString(UUID_LENGTH); + // 版本号 + int Version = GetInt(); + (void)Version; + // 文件路径数据的大小 + int AlignedIndexHeaderSize = GetInt(); + // 解密密钥 + int IndexHeaderCrc = GetInt(); + // 文件数量 + int IndexSize = GetInt(); + // 文件起始位置 + int FristPos = tellg(); + CrcDecode(AlignedIndexHeaderSize, IndexHeaderCrc); + int CurrPos = 0; + StartPos = AlignedIndexHeaderSize + 56; + // 建立pvf文件索引表 + for (int i = 0; i < IndexSize; i++) + { + seek(FristPos + CurrPos); + int FileNumber = GetInt(); + (void)FileNumber; + int FilePathLength = GetInt(); + std::string FileName = tolower(GetString(FilePathLength)); + int FileLength = GetInt(); + int Cre32 = GetInt(); + int RelativeOffset = GetInt(); + if (FileLength > 0) + { + int RealFileLength = (FileLength + 3) & 4294967292; + PvfFileInfo Info; + Info.ROffset = RelativeOffset; + Info.Cr32 = Cre32; + Info.Length = RealFileLength; + Info.DecodeFlag = false; + FileInfo[FileName] = Info; + } + CurrPos += 20; + CurrPos += FilePathLength; + } +} + +void Asset_Script::InitBin() +{ + if (FileInfo.count("stringtable.bin") == 0) + { + SDL_LogError(0, "stringtable.bin文件不存在"); + return; + } + PvfFileInfo BinInfo = FileInfo["stringtable.bin"]; + seek(StartPos + BinInfo.ROffset); + CrcDecode(BinInfo.Length, BinInfo.Cr32); + seek(StartPos + BinInfo.ROffset); + int FileHPos = tellg(); + int Count = GetInt(); + int CurrentIndex = 0; + + for (int i = 0; i < Count; i++) + { + seek(FileHPos + CurrentIndex * 4 + 4); + int StartPos = GetInt(); + int EndPos = GetInt(); + int Len = EndPos - StartPos; + seek(FileHPos + StartPos + 4); + std::string Str = GetString(Len); + BinStringM[CurrentIndex] = Str; + CurrentIndex++; + } +} + +void Asset_Script::InitLoadString() +{ + if (FileInfo.count("n_string.lst") == 0) + { + SDL_LogError(0, "n_string.lst文件不存在"); + } + PvfFileInfo Info = FileInfo["n_string.lst"]; + seek(StartPos + Info.ROffset); + CrcDecode(Info.Length, Info.Cr32); + seek(StartPos + Info.ROffset); + + int FileHPos = tellg(); + int Flag = GetShort(); + (void)Flag; + int i = 2; + while (i < Info.Length) + { + if ((Info.Length - i) >= 10) + { + seek(FileHPos + i + 6); + int FindKey = GetInt(); + std::string Key = GetBinString(FindKey); + std::string Type = tolower(Key.substr(0, Key.find("/"))); + if (Key.length() > 0) + { + PvfFileInfo *FileInfo = GetFileInfo(Key); + if (FileInfo == nullptr) + continue; + + seek(StartPos + FileInfo->ROffset); + CrcDecode(FileInfo->Length, FileInfo->Cr32); + seek(StartPos + FileInfo->ROffset); + + std::string Str = GetStringNormal(FileInfo->Length); + std::vector StrArr = split(Str, "\n"); + for (auto it = StrArr.begin(); it != StrArr.end(); ++it) + { + std::string strobj = *it; + if (strobj.find(">") != std::string::npos) + { + std::vector strobjarr = split(strobj, ">"); + if (strobjarr.size() > 1) + { + LoadStringM[Type][strobjarr[0]] = strobjarr[1]; + } + } + } + } + } + else + break; + i += 10; + } +} + +std::string Asset_Script::GetBinString(int Key) +{ + if (BinStringM.count(Key)) + return BinStringM[Key]; + return ""; +} + +std::string Asset_Script::GetLoadString(std::string Type, std::string Key) +{ + if (LoadStringM.count(Type) && LoadStringM[Type].count(Key)) + return LoadStringM[Type][Key]; + return ""; +} + +Asset_Script::PvfFileInfo *Asset_Script::GetFileInfo(std::string path) +{ + path = tolower(path); + if (FileInfo.count(path)) + return &FileInfo[path]; + return nullptr; +} + +std::string Asset_Script::GetFileContent(std::string path) +{ + if (FileInfo.count(path)) + { + seek(StartPos + FileInfo[path].ROffset); + if (FileInfo[path].DecodeFlag == false) + { + CrcDecode(FileInfo[path].Length, FileInfo[path].Cr32); + seek(StartPos + FileInfo[path].ROffset); + FileInfo[path].DecodeFlag = true; + } + char *blobp = new char[FileInfo[path].Length]; + read((char *)blobp, FileInfo[path].Length); + std::string Str(blobp); + delete[] blobp; + return Str; + } + return std::string(); +} + +std::vector Asset_Script::GetFileContentByte(std::string path) +{ + if (FileInfo.count(path)) + { + seek(StartPos + FileInfo[path].ROffset); + if (FileInfo[path].DecodeFlag == false) + { + CrcDecode(FileInfo[path].Length, FileInfo[path].Cr32); + seek(StartPos + FileInfo[path].ROffset); + FileInfo[path].DecodeFlag = true; + } + std::vector blobp(FileInfo[path].Length); + read((char *)blobp.data(), FileInfo[path].Length); + return blobp; + } + return std::vector(); +} diff --git a/source/EngineCore/Asset_Script.h b/source/EngineCore/Asset_Script.h new file mode 100644 index 0000000..3818952 --- /dev/null +++ b/source/EngineCore/Asset_Script.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include "Tool/Ifstream_PVF.h" + +class Asset_Script : public Ifstream_PVF +{ +public: + struct PvfFileInfo + { + int ROffset; + int Cr32; + int Length; + bool DecodeFlag = false; + }; + +private: + Asset_Script(const std::string filename = "Script.pvf") : Ifstream_PVF(filename) {}; + ~Asset_Script() = default; + + int StartPos = 0; + std::map FileInfo; + std::map BinStringM; + std::map> LoadStringM; + +public: + Asset_Script(const Asset_Script &) = delete; + Asset_Script &operator=(const Asset_Script &) = delete; + Asset_Script(Asset_Script &&) = delete; + Asset_Script &operator=(Asset_Script &&) = delete; + // 全局访问点 + static Asset_Script &GetInstance() + { + static Asset_Script instance; // 局部静态变量,保证只初始化一次 + return instance; + } + +public: + void Init(); + void InitHeader(); + void InitBin(); + void InitLoadString(); + void Clear(); + +public: + std::string + GetBinString(int Key); + std::string GetLoadString(std::string Type, std::string Key); + PvfFileInfo *GetFileInfo(std::string path); + + std::string GetFileContent(std::string path); // 获取文件内容 + std::vector GetFileContentByte(std::string path); // 获取文件内容 + + bool InitFlag = false; +}; diff --git a/source/EngineCore/Game.cpp b/source/EngineCore/Game.cpp new file mode 100644 index 0000000..4f54d5d --- /dev/null +++ b/source/EngineCore/Game.cpp @@ -0,0 +1,204 @@ +#include "Game.h" +#include "squirrel/SquirrelEx.h" +#include "EngineFrame/Component/Sprite.h" +#include "EngineFrame/Actor/Actor.h" +#include "EngineFrame/Component/Text.h" +#include "EngineFrame/Actor/Debug_Actor.h" + +Game::Game() +{ +} + +Game::~Game() +{ + Clear(); +} + +void Game::Init(std::function CallBack) +{ + // 计算帧时间 + m_frameTime = 1000 / m_fps; + + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + SDL_JoystickEventState(SDL_ENABLE); + SDL_JoystickOpen(0); + + if (SDL_Init(SDL_INIT_EVERYTHING) < 0) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL could not initialize! Error: %s\n", SDL_GetError()); + m_isRunning = false; + } + m_window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Screen_W, Screen_H, SDL_WINDOW_SHOWN); + if (m_window == nullptr) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL could not Create Window! Error: %s\n", SDL_GetError()); + m_isRunning = false; + } + + // 创建渲染器 + m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED); + if (m_renderer == nullptr) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL could not Create Renderer! Error: %s\n", SDL_GetError()); + m_isRunning = false; + } + // 启用渲染器的混合功能(必须,否则纹理混合模式无效) + SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_BLEND); + IMG_Init(IMG_INIT_PNG); + + // 初始化SDL_mixer,支持OGG格式 + // 44100: 采样率, MIX_DEFAULT_FORMAT: 音频格式, 2: 声道数(立体声), 4096: 缓冲区大小 + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) < 0) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_mixer初始化失败! Mix_Error: %s\n", Mix_GetError()); + m_isRunning = false; + } + + // 初始化 TTF + if (TTF_Init() == -1) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF 初始化失败!TTF_Error: %s\n", TTF_GetError()); + m_isRunning = false; + } + // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + + m_DebugInfoActor = new Debug_Actor(); + CallBack(); +} + +void Game::Run() +{ + while (m_isRunning) + { + // 帧开始时间 + u32 frameStart = SDL_GetTicks(); + SDL_Event m_event; + HandleEvents(&m_event); + Update(m_deltaTime); + Render(m_deltaTime); + + // -------------------------- 新增:帧率统计与输出 -------------------------- + m_frameCount++; // 每帧递增计数器 + u32 currentTime = SDL_GetTicks(); + // 判断是否已过去 1 秒(1000 毫秒) + if (currentTime - m_lastFpsPrintTime >= 1000) + { + // 计算帧率:总帧数 / 1秒 = 帧率(如 60 帧 → 60FPS) + u32 fps = m_frameCount; + // 输出帧率(用 SDL_Log 或 printf 均可,SDL_Log 更适配 SDL 日志系统) + // SDL_Log("当前帧率:%d FPS | DeltaTime:%.4f 秒", fps, m_deltaTime); + if (m_DebugInfoActor) + m_DebugInfoActor->FPS = fps; + + // 重置统计:更新“上一次输出时间”,重置“帧数计数器” + m_lastFpsPrintTime = currentTime; + m_frameCount = 0; + } + // -------------------------------------------------------------------------- + + // 原有逻辑:控制帧间隔(保留,无需修改) + u32 diff = SDL_GetTicks() - frameStart; + if (diff < m_frameTime) + { + SDL_Delay(m_frameTime - diff); + m_deltaTime = m_frameTime / 1000.0f; + } + else + { + m_deltaTime = diff / 1000.0f; + } + } +} + +void Game::HandleEvents(SDL_Event *e) +{ + while (SDL_PollEvent(e)) + { + if (e->type == SDL_QUIT) + { + m_isRunning = false; + SDL_Log("Game Exit1"); + } + else if (e->type == SDL_JOYBUTTONDOWN) + { + if (e->jbutton.button == JOY_PLUS) + { + m_isRunning = false; + SDL_Log("Game Exit2"); + } + } + + if (m_scene != nullptr) + m_scene->HandleEvents(e); + if (m_uiScene != nullptr) + m_uiScene->HandleEvents(e); + } +} + +void Game::Update(float deltaTime) +{ + if (m_scene != nullptr) + m_scene->Update(deltaTime); + if (m_uiScene != nullptr) + m_uiScene->Update(deltaTime); + + // 调试信息 + if (m_DebugInfoActor != nullptr) + m_DebugInfoActor->Update(deltaTime); +} + +void Game::Render(float deltaTime) +{ + SDL_RenderClear(m_renderer); + if (m_scene != nullptr) + m_scene->Render(deltaTime); + if (m_uiScene != nullptr) + m_uiScene->Render(deltaTime); + if (m_DebugInfoActor != nullptr) + m_DebugInfoActor->Render(deltaTime); + SDL_RenderPresent(m_renderer); +} + +void Game::Clear() +{ + if (m_scene != nullptr) + { + m_scene->Exit(); + } + m_scene = nullptr; + if (m_uiScene != nullptr) + { + m_uiScene->Exit(); + } + m_uiScene = nullptr; + m_DebugInfoActor = nullptr; + IMG_Quit(); + SDL_DestroyRenderer(m_renderer); + SDL_DestroyWindow(m_window); + SDL_Quit(); +} + +void Game::ChangeScene(RefPtr scene) +{ + if (m_scene != nullptr) + { + m_scene->Exit(); + } + m_scene = scene; + m_scene->Enter(); +} + +void Game::ChangeUIScene(RefPtr scene) +{ + if (m_uiScene != nullptr) + { + m_uiScene->Exit(); + } + m_uiScene = scene; + m_uiScene->Enter(); +} + +SDL_Renderer *Game::GetRenderer() +{ + return m_renderer; +} diff --git a/source/EngineCore/Game.h b/source/EngineCore/Game.h new file mode 100644 index 0000000..77f673b --- /dev/null +++ b/source/EngineCore/Game.h @@ -0,0 +1,91 @@ +#pragma once +#include "EngineFrame/Scene/Scene.h" +#include "Tool/RefPtr.h" + +#include +#include +#include +#include +#include +#include +#include + +// some switch buttons +#define JOY_A 0 +#define JOY_B 1 +#define JOY_X 2 +#define JOY_Y 3 +#define JOY_PLUS 10 +#define JOY_MINUS 11 +#define JOY_LEFT 12 +#define JOY_UP 13 +#define JOY_RIGHT 14 +#define JOY_DOWN 15 + +class Debug_Actor; + +class Game +{ + +public: + // 删除拷贝构造和赋值运算符,确保无法复制 + Game(const Game &) = delete; + Game &operator=(const Game &) = delete; + + // 移动构造和赋值也删除,避免意外转移 + Game(Game &&) = delete; + Game &operator=(Game &&) = delete; + + // 全局访问点 + static Game &GetInstance() + { + static Game instance; // 局部静态变量,保证只初始化一次 + return instance; + } + + void Init(std::function CallBack); + void Run(); + void HandleEvents(SDL_Event *e); + void Update(float deltaTime); + void Render(float deltaTime); + void Clear(); + + // 切换场景 + void ChangeScene(RefPtr scene); + // 设定UI层场景对象 + void ChangeUIScene(RefPtr scene); + + SDL_Renderer *GetRenderer(); + +private: + // 构造函数和析构函数设为私有,防止外部创建和销毁 + Game(); + ~Game(); + + // 游戏是否运行 + bool m_isRunning = true; + // 窗口 + SDL_Window *m_window; + // 渲染器 + SDL_Renderer *m_renderer; + + // 游戏层场景 + RefPtr m_scene; + // UI层场景 + RefPtr m_uiScene; + + // 屏幕宽高 + int Screen_W = 1280; + int Screen_H = 720; + + // 帧数 + int m_fps = 10000; + u32 m_frameTime; + float m_deltaTime; + // 新增:帧率统计变量 + u32 m_frameCount; // 每秒内的帧数计数器 + u32 m_lastFpsPrintTime; // 上一次输出帧率的时间(毫秒,基于 SDL_GetTicks()) + + // 调试信息Actor + RefPtr m_DebugInfoActor; +}; \ No newline at end of file diff --git a/source/EngineFrame/Actor/Actor.cpp b/source/EngineFrame/Actor/Actor.cpp new file mode 100644 index 0000000..f4e833d --- /dev/null +++ b/source/EngineFrame/Actor/Actor.cpp @@ -0,0 +1,50 @@ +#include "Actor.h" +#include "EngineFrame/Scene/Scene.h" + +Actor::Actor() +{ +} + +Actor::~Actor() +{ +} + +void Actor::Init() +{ +} + +void Actor::HandleEvents(SDL_Event *e) +{ +} + +void Actor::Update(float deltaTime) +{ + Actor_base::Update(deltaTime); + RefPtr child = m_Components.GetFirst(); + while (child) + { + child->Update(deltaTime); + child = child->GetNext(); + } +} + +void Actor::Render(float deltaTime) +{ + Actor_base::Render(deltaTime); + RefPtr child = m_Components.GetFirst(); + while (child) + { + child->Render(deltaTime); + child = child->GetNext(); + } +} + +void Actor::Clear() +{ +} + +void Actor::AddComponent(RefPtr Component) +{ + m_Components.PushBack(Component); + Component->Parent = this; +} \ No newline at end of file diff --git a/source/EngineFrame/Actor/Actor.h b/source/EngineFrame/Actor/Actor.h new file mode 100644 index 0000000..0468b28 --- /dev/null +++ b/source/EngineFrame/Actor/Actor.h @@ -0,0 +1,35 @@ +#pragma once +#include "EngineFrame/Actor/Actor_base.h" +#include "EngineFrame/Component/Component.h" +#include "Tool/IntrusiveList.hpp" +class Scene; +/** + * @brief Actor类,继承自Actor_base类 + * + * Actor类是一个基础的游戏对象类,可以添加到场景中 + */ +class Actor : public Actor_base, protected IntrusiveListValue> +{ +public: + Actor(); + ~Actor() override; + +public: + void Init() override; + void HandleEvents(SDL_Event *e) override; + void Update(float deltaTime) override; + void Render(float deltaTime) override; + void Clear() override; + + using IntrusiveListValue>::GetNext; + using IntrusiveListValue>::GetPrev; + +public: + void AddComponent(RefPtr Component); + +public: + Scene *Parent; // 指向父场景的指针,表示该Actor所属的场景 + +private: + IntrusiveList> m_Components; +}; diff --git a/source/EngineFrame/Actor/Actor_base.cpp b/source/EngineFrame/Actor/Actor_base.cpp new file mode 100644 index 0000000..0bf4131 --- /dev/null +++ b/source/EngineFrame/Actor/Actor_base.cpp @@ -0,0 +1,29 @@ +#include "Actor_base.h" + +void Actor_base::Init() +{ +} +void Actor_base::HandleEvents(SDL_Event *e) +{ +} + +void Actor_base::Update(float deltaTime) +{ + if (cb_update_) + { + cb_update_(deltaTime); + } +} + +void Actor_base::Render(float deltaTime) +{ +} + +void Actor_base::Clear() +{ +} + +void Actor_base::SetCallbackOnUpdate(const UpdateCallback &cb) +{ + cb_update_ = cb; +} diff --git a/source/EngineFrame/Actor/Actor_base.h b/source/EngineFrame/Actor/Actor_base.h new file mode 100644 index 0000000..469e8d3 --- /dev/null +++ b/source/EngineFrame/Actor/Actor_base.h @@ -0,0 +1,31 @@ +#pragma once +#include "Tool/RefPtr.h" +#include "Tool/RefObject.h" +#include +#include +class Actor_base : public RefObject +{ + +public: + /// \~chinese + /// @brief 角色更新回调函数 + typedef std::function UpdateCallback; + +public: + Actor_base(/* args */) = default; + virtual ~Actor_base() = default; + + virtual void Init(); + virtual void HandleEvents(SDL_Event *e); + virtual void Update(float deltaTime); + virtual void Render(float deltaTime); + virtual void Clear(); + + /// \~chinese + /// @brief 设置更新时的回调函数 + void SetCallbackOnUpdate(const UpdateCallback &cb); + +private: + /* data */ + UpdateCallback cb_update_; // 更新时的回调函数 +}; diff --git a/source/EngineFrame/Actor/Debug_Actor.cpp b/source/EngineFrame/Actor/Debug_Actor.cpp new file mode 100644 index 0000000..87341f1 --- /dev/null +++ b/source/EngineFrame/Actor/Debug_Actor.cpp @@ -0,0 +1,146 @@ +#include "Debug_Actor.h" +#include "EngineCore/Game.h" +#include + +Debug_Actor::Debug_Actor() +{ + m_debugFont = TTF_OpenFont("Fonts/GasinamuNew.ttf", 16); + if (m_debugFont == nullptr) + { + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load debug font: %s", TTF_GetError()); + } + + FPS_Text = new Text("Current FPS:", m_debugFont, SDL_Color{255, 255, 255, 255}); + DT_Text = new Text("DeltaTime :", m_debugFont, SDL_Color{255, 255, 255, 255}); + if (FPS_Text != nullptr) + { + SDL_Point Pos{26, 26}; + FPS_Text->SetPos(Pos); + this->AddComponent(FPS_Text); + } + if (DT_Text != nullptr) + { + SDL_Point Pos{26, 46}; + DT_Text->SetPos(Pos); + this->AddComponent(DT_Text); + } +} + +Debug_Actor::~Debug_Actor() +{ + + if (m_debugFont != nullptr) + { + TTF_CloseFont(m_debugFont); + m_debugFont = nullptr; + } +} + +void Debug_Actor::Update(float deltaTime) +{ + Actor::Update(deltaTime); + + if (FPS_Text != nullptr) + { + std::string fpsText = "Current FPS: " + std::to_string(FPS); + FPS_Text->SetText(fpsText); + std::string dtText = "DeltaTime: " + std::to_string((int)(deltaTime * 1000)); + DT_Text->SetText(dtText); + } +} + +void Debug_Actor::Render(float deltaTime) +{ + if (FPS_Text != nullptr) + { + SDL_Renderer *renderer = Game::GetInstance().GetRenderer(); + if (renderer != nullptr) + { + + SDL_Point textPos = FPS_Text->Pos; + SDL_Point textSize = FPS_Text->Size; + + int bgX = textPos.x - padding; + int bgY = textPos.y - padding; + int bgWidth = textSize.x + padding * 2; + int bgHeight = textSize.y + padding * 2 * 4; + + DrawRoundedRect(renderer, bgX, bgY, bgWidth, bgHeight, cornerRadius, bgColor); + } + } + + Actor::Render(deltaTime); +} + +void Debug_Actor::DrawRoundedRect(SDL_Renderer *renderer, int x, int y, int w, int h, int radius, SDL_Color color) +{ + if (renderer == nullptr) + return; + + SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); + + SDL_RenderDrawLine(renderer, x + radius, y, x + w - radius, y); + SDL_RenderDrawLine(renderer, x + radius, y + h, x + w - radius, y + h); + SDL_RenderDrawLine(renderer, x, y + radius, x, y + h - radius); + SDL_RenderDrawLine(renderer, x + w, y + radius, x + w, y + h - radius); + + const int segments = 16; + for (int i = 0; i < segments; i++) + { + + float t1 = static_cast(i) / segments; + float t2 = static_cast(i + 1) / segments; + + float angle1 = M_PI * (1.0f - t1); + float angle2 = M_PI * (1.0f - t2); + SDL_RenderDrawLineF( + renderer, + x + radius + cos(angle1) * radius, y + radius + sin(angle1) * radius, + x + radius + cos(angle2) * radius, y + radius + sin(angle2) * radius); + + angle1 = 2 * M_PI - t1 * M_PI_2; + angle2 = 2 * M_PI - t2 * M_PI_2; + SDL_RenderDrawLineF( + renderer, + x + w - radius + cos(angle1) * radius, y + radius + sin(angle1) * radius, + x + w - radius + cos(angle2) * radius, y + radius + sin(angle2) * radius); + + angle1 = t1 * M_PI_2; + angle2 = t2 * M_PI_2; + SDL_RenderDrawLineF( + renderer, + x + w - radius + cos(angle1) * radius, y + h - radius + sin(angle1) * radius, + x + w - radius + cos(angle2) * radius, y + h - radius + sin(angle2) * radius); + + angle1 = M_PI_2 + t1 * M_PI_2; + angle2 = M_PI_2 + t2 * M_PI_2; + SDL_RenderDrawLineF( + renderer, + x + radius + cos(angle1) * radius, y + h - radius + sin(angle1) * radius, + x + radius + cos(angle2) * radius, y + h - radius + sin(angle2) * radius); + } + + for (int dy = 0; dy < h; dy++) + { + int leftIndent = 0; + int rightIndent = 0; + + if (dy < radius) + { + float ratio = 1.0f - static_cast(dy) / radius; + leftIndent = rightIndent = static_cast(radius * (1.0f - sqrt(1.0f - ratio * ratio))); + } + + else if (dy > h - radius) + { + float ratio = static_cast(dy - (h - radius)) / radius; + leftIndent = rightIndent = static_cast(radius * (1.0f - sqrt(1.0f - ratio * ratio))); + } + + SDL_RenderDrawLine( + renderer, + x + leftIndent, y + dy, + x + w - rightIndent, y + dy); + } +} \ No newline at end of file diff --git a/source/EngineFrame/Actor/Debug_Actor.h b/source/EngineFrame/Actor/Debug_Actor.h new file mode 100644 index 0000000..a19c3d6 --- /dev/null +++ b/source/EngineFrame/Actor/Debug_Actor.h @@ -0,0 +1,32 @@ +#pragma once +#include "EngineFrame/Actor/Actor.h" +#include "EngineFrame/Component/Text.h" +#include +#include + +class Debug_Actor : public Actor +{ +private: + TTF_Font *m_debugFont; + RefPtr FPS_Text; + RefPtr DT_Text; + +public: + Debug_Actor(); + ~Debug_Actor() override; + + void Update(float deltaTime) override; + void Render(float deltaTime) override; + + uint32_t FPS = 0; + + SDL_Color bgColor = {0, 0, 0, 90}; + + int cornerRadius = 4; + int padding = 12; + double M_PI = 3.14159265358979323846; + double M_PI_2 = 1.57079632679489661923; + +private: + void DrawRoundedRect(SDL_Renderer *renderer, int x, int y, int w, int h, int radius, SDL_Color color); +}; \ No newline at end of file diff --git a/source/EngineFrame/Component/Component.cpp b/source/EngineFrame/Component/Component.cpp new file mode 100644 index 0000000..7b05154 --- /dev/null +++ b/source/EngineFrame/Component/Component.cpp @@ -0,0 +1,33 @@ +#include "Component.h" + +Component::Component() +{ +} + +Component::~Component() +{ +} + +void Component::Init() +{ +} +void Component::HandleEvents(SDL_Event *e) +{ +} + +void Component::Update(float deltaTime) +{ +} + +void Component::Render(float deltaTime) +{ +} + +void Component::Clear() +{ +} + +void Component::SetName(std::string name) +{ + m_Name = name; +} diff --git a/source/EngineFrame/Component/Component.h b/source/EngineFrame/Component/Component.h new file mode 100644 index 0000000..73a8beb --- /dev/null +++ b/source/EngineFrame/Component/Component.h @@ -0,0 +1,31 @@ +#pragma once +#include "Tool/IntrusiveList.hpp" +#include "Tool/RefPtr.h" +#include "Tool/RefObject.h" +#include "Tool/TagGed.h" + +#include +class Actor; +class Component : public RefObject, public TagGed, protected IntrusiveListValue> +{ +private: + /* data */ + std::string m_Name; +public: + Component(/* args */); + ~Component(); + + virtual void Init(); + virtual void HandleEvents(SDL_Event *e); + virtual void Update(float deltaTime); + virtual void Render(float deltaTime); + virtual void Clear(); + + using IntrusiveListValue>::GetNext; + using IntrusiveListValue>::GetPrev; + + void SetName(std::string name); + +public: + Actor *Parent; // 指向父对象的指针,用于访问父对象 +}; diff --git a/source/EngineFrame/Component/Sprite.cpp b/source/EngineFrame/Component/Sprite.cpp new file mode 100644 index 0000000..64232d9 --- /dev/null +++ b/source/EngineFrame/Component/Sprite.cpp @@ -0,0 +1,122 @@ +#include "Sprite.h" +#include "EngineCore/Game.h" +#include "Text.h" +Sprite::Sprite() +{ +} + +Sprite::Sprite(std::string imgPath, int Index) +{ + Init(imgPath, Index); +} + +Sprite::~Sprite() +{ + SDL_DestroyTexture(m_texture); +} + +void Sprite::Init(std::string imgPath, int Index) +{ + Asset_ImagePack::IMG *Info = Asset_ImagePack::GetInstance().GetIMG(imgPath); + Asset_ImagePack::ImgInfo &Buf = Info->lp_lplist[Index]; + + m_texture = SDL_CreateTexture( + Game::GetInstance().GetRenderer(), + SDL_PIXELFORMAT_ARGB8888, // 匹配RGBA数据格式 + SDL_TEXTUREACCESS_STREAMING, + Buf.Width, Buf.Height); + if (!m_texture) + { + SDL_Log("纹理创建失败: %s", SDL_GetError()); + } + int pitch = Buf.Width * 4; + SDL_UpdateTexture(m_texture, NULL, Buf.PNGdata, pitch); + + if (Info != NULL) + { + // SDL_Log("第%d张图片的宽度为%d,高度为%d\n", Index, Buf.Width, Buf.Height); + } + TextureSize.x = Buf.Width; + TextureSize.y = Buf.Height; + Size.x = Buf.Width; + Size.y = Buf.Height; +} + +SDL_Texture *Sprite::GetTexture() +{ + return m_texture; +} + +void Sprite::HandleEvents(SDL_Event *e) +{ +} + +void Sprite::Update(float deltaTime) +{ +} + +void Sprite::Render(float deltaTime) +{ + SDL_Renderer *renderer = Game::GetInstance().GetRenderer(); + if (!m_texture) + return; + SDL_Rect dstrect = {Pos.x, Pos.y, Size.x, Size.y}; + + if (Angle != 0 || flip != SDL_FLIP_NONE) + { + SDL_RenderCopyEx(renderer, m_texture, NULL, &dstrect, Angle, &Anchor, flip); + } + else + SDL_RenderCopy(renderer, m_texture, NULL, &dstrect); +} + +void Sprite::Clear() +{ +} + +void Sprite::SetPos(SDL_Point pos) +{ + Pos = pos; +} + +void Sprite::SetBlendMode(SDL_BlendMode blendMode) +{ + SDL_SetTextureBlendMode(m_texture, blendMode); +} + +void Sprite::SetAngle(float angle) +{ + Angle = angle; +} + +void Sprite::SetAnchor(SDL_FPoint anchor) +{ + Anchor.x = Size.x * anchor.x; + Anchor.y = Size.y * anchor.y; +} + +SDL_Point Sprite::GetPos() +{ + return Pos; +} + + +SDL_BlendMode Sprite::GetBlendMode() +{ + SDL_BlendMode blendMode; + SDL_GetTextureBlendMode(m_texture, &blendMode); + return blendMode; +} + +float Sprite::GetAngle() +{ + return Angle; +} + +SDL_FPoint Sprite::GetAnchor() +{ + SDL_FPoint P; + P.x = Anchor.x / Size.x; + P.y = Anchor.y / Size.y; + return P; +} diff --git a/source/EngineFrame/Component/Sprite.h b/source/EngineFrame/Component/Sprite.h new file mode 100644 index 0000000..709025f --- /dev/null +++ b/source/EngineFrame/Component/Sprite.h @@ -0,0 +1,95 @@ +#pragma once +#include +#include "EngineCore/Asset_ImagePack.h" +#include "EngineFrame/Component/Component.h" + +class Game; +/** + * @brief Sprite类,继承自Component类,用于表示游戏中的精灵组件 + */ +class Sprite : public Component +{ +private: + /* data */ + SDL_Texture *m_texture = nullptr; // 纹理指针,用于存储精灵的纹理资源 + +public: + /** + * @brief Sprite类的默认构造函数 + */ + Sprite(/* args */); + /** + * @brief Sprite类的带参数构造函数 + * @param imgPath 纹理图片路径 + * @param Index 索引值 + */ + Sprite(std::string imgPath, int Index); + /** + * @brief Sprite类的析构函数 + */ + ~Sprite(); + + // 显式引入基类的Init方法,避免隐藏 + using Component::Init; + /** + * @brief 初始化Sprite组件 + * @param imgPath 纹理图片路径 + * @param Index 索引值 + */ + void Init(std::string imgPath, int Index); + /** + * @brief 处理事件 + * @param e SDL事件指针 + */ + void HandleEvents(SDL_Event *e) override; + /** + * @brief 更新组件状态 + * @param deltaTime 时间增量 + */ + void Update(float deltaTime) override; + /** + * @brief 渲染组件 + * @param deltaTime 时间增量 + */ + void Render(float deltaTime) override; + /** + * @brief 清理组件资源 + */ + void Clear() override; + + /** + * @brief 获取纹理 + * @return SDL_Texture* 纹理指针 + */ + SDL_Texture *GetTexture(); + +public: + // 组件标签 + Tag m_tag = Tag::RENDER | Tag::UPDATE; // 标记该组件需要渲染和更新 + + SDL_Point Pos = {0, 0}; // 位置坐标 + SDL_Point TextureSize = {0, 0}; // 纹理大小 + SDL_Point Size = {0, 0}; // 大小 + SDL_Point Anchor = {0, 0}; // 中心点 + float Angle = 0.0f; // 旋转角度 + SDL_RendererFlip flip = SDL_FLIP_NONE; // 翻转 + +public: + // 设置坐标 + void SetPos(SDL_Point pos); + // 设置混合模式 + void SetBlendMode(SDL_BlendMode blendMode); + // 设置旋转角度 + void SetAngle(float angle); + //设置中心点 + void SetAnchor(SDL_FPoint anchor); + + // 获取坐标 + SDL_Point GetPos(); + // 获取混合模式 + SDL_BlendMode GetBlendMode(); + // 获取旋转角度 + float GetAngle(); + // 获取中心点 + SDL_FPoint GetAnchor(); +}; diff --git a/source/EngineFrame/Component/Text.cpp b/source/EngineFrame/Component/Text.cpp new file mode 100644 index 0000000..62c67c4 --- /dev/null +++ b/source/EngineFrame/Component/Text.cpp @@ -0,0 +1,203 @@ +#include "EngineFrame/Component/Text.h" +#include "Text.h" +#include "EngineCore/Game.h" +Text::Text() +{ +} + +Text::Text(std::string Str, TTF_Font *font, SDL_Color color) +{ + m_text = Str; + m_font = font; + m_text_color = color; + Init(Str, font, color); +} + +Text::Text(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize) +{ + m_text = Str; + m_font = font; + m_text_color = textColor; + m_stroke_color = strokeColor; + m_stroke_size = strokeSize; + Init(Str, font, textColor, strokeColor, strokeSize); +} + +Text::~Text() +{ +} + +void Text::Init(std::string Str, TTF_Font *font, SDL_Color color) +{ + // TTF_SetFontOutline(font, 1); + // 先渲染为表面 + SDL_Surface *textSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), color); + if (!textSurface) + { + SDL_LogError(0, "文字渲染为表面失败!TTF_Error:%s", TTF_GetError()); + } + // 再将表面转换为纹理 + SDL_Renderer *renderer = Game::GetInstance().GetRenderer(); + m_texture = SDL_CreateTextureFromSurface(renderer, textSurface); + if (!m_texture) + { + SDL_LogError(0, "表面转换为纹理失败!SDL_Error:%s", SDL_GetError()); + } + // 设置纹理过滤模式为最近邻,避免缩放模糊 + SDL_SetTextureScaleMode(m_texture, SDL_ScaleModeNearest); + Size.x = textSurface->w; + Size.y = textSurface->h; + TextureSize.x = textSurface->w; + TextureSize.y = textSurface->h; + + // 释放表面 + SDL_FreeSurface(textSurface); +} + +void Text::Init(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize) +{ + // 先保存原始字体的轮廓设置 + int originalOutline = TTF_GetFontOutline(font); + + // 设置字体轮廓大小(描边宽度) + TTF_SetFontOutline(font, strokeSize); + + // 渲染描边(使用描边颜色) + SDL_Surface *strokeSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), strokeColor); + if (!strokeSurface) + { + SDL_LogError(0, "描边渲染为表面失败!TTF_Error:%s", TTF_GetError()); + TTF_SetFontOutline(font, originalOutline); // 恢复原始设置 + return; + } + + // 恢复字体轮廓设置,用于渲染文字本身 + TTF_SetFontOutline(font, 0); + + // 渲染文字本身(使用文字颜色) + SDL_Surface *textSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), textColor); + if (!textSurface) + { + SDL_LogError(0, "文字渲染为表面失败!TTF_Error:%s", TTF_GetError()); + SDL_FreeSurface(strokeSurface); + TTF_SetFontOutline(font, originalOutline); // 恢复原始设置 + return; + } + + // 创建一个合并描边和文字的表面 + SDL_Renderer *renderer = Game::GetInstance().GetRenderer(); + + // 计算最终纹理大小(描边会增加额外尺寸) + int finalWidth = strokeSurface->w; + int finalHeight = strokeSurface->h; + + // 创建一个临时表面用于合并描边和文字 + SDL_Surface *finalSurface = SDL_CreateRGBSurfaceWithFormat( + 0, finalWidth, finalHeight, 32, SDL_PIXELFORMAT_RGBA32); + if (!finalSurface) + { + SDL_LogError(0, "创建最终表面失败!SDL_Error:%s", SDL_GetError()); + SDL_FreeSurface(textSurface); + SDL_FreeSurface(strokeSurface); + TTF_SetFontOutline(font, originalOutline); + return; + } + + // 将描边绘制到最终表面 + SDL_Rect strokeRect = {0, 0, strokeSurface->w, strokeSurface->h}; + SDL_BlitSurface(strokeSurface, nullptr, finalSurface, &strokeRect); + + // 计算文字在描边中间的位置 + SDL_Rect textRect = { + strokeSize, // X偏移(描边宽度) + strokeSize, // Y偏移(描边宽度) + textSurface->w, + textSurface->h}; + SDL_BlitSurface(textSurface, nullptr, finalSurface, &textRect); + + // 将合并后的表面转换为纹理 + m_texture = SDL_CreateTextureFromSurface(renderer, finalSurface); + if (!m_texture) + { + SDL_LogError(0, "表面转换为纹理失败!SDL_Error:%s", SDL_GetError()); + } + + // 设置尺寸信息 + Size.x = finalSurface->w; + Size.y = finalSurface->h; + TextureSize.x = finalSurface->w; + TextureSize.y = finalSurface->h; + + // 释放所有临时表面 + SDL_FreeSurface(textSurface); + SDL_FreeSurface(strokeSurface); + SDL_FreeSurface(finalSurface); + + // 恢复字体原始轮廓设置 + TTF_SetFontOutline(font, originalOutline); +} + +void Text::HandleEvents(SDL_Event *e) +{ +} + +void Text::Update(float deltaTime) +{ +} + +void Text::Render(float deltaTime) +{ + SDL_Renderer *renderer = Game::GetInstance().GetRenderer(); + if (!renderer || !m_texture) + return; + SDL_Rect dstrect = {Pos.x, Pos.y, Size.x, Size.y}; + + SDL_RenderCopy(renderer, m_texture, NULL, &dstrect); +} + +void Text::Clear() +{ +} + +void Text::SetPos(SDL_Point pos) +{ + Pos = pos; +} + +SDL_Point Text::GetPos() +{ + return Pos; +} + +void Text::SetText(std::string Str) +{ + if (Str == m_text) + return; + if (!m_font) + { + SDL_LogError(0, "SetText失败:字体指针为空!"); + return; + } + + // 如果有原纹理先删除原纹理 + if (m_texture) + { + SDL_DestroyTexture(m_texture); + m_texture = nullptr; // 置空指针 + } + m_text = Str; + // 根据是否有描边选择对应的Init方法 + if (m_stroke_size > 0) + { + Init(Str, m_font, m_text_color, m_stroke_color, m_stroke_size); + } + else + { + Init(Str, m_font, m_text_color); + } +} + +std::string Text::GetText() +{ + return m_text; +} \ No newline at end of file diff --git a/source/EngineFrame/Component/Text.h b/source/EngineFrame/Component/Text.h new file mode 100644 index 0000000..0a26fd4 --- /dev/null +++ b/source/EngineFrame/Component/Text.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include "EngineFrame/Component/Component.h" +class Game; + +class Text : public Component +{ +private: + /* data */ + SDL_Texture *m_texture = nullptr; + +public: + Text(/* args */); + Text(std::string Str, TTF_Font *font, SDL_Color color); + Text(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize); + ~Text(); + + // 显式引入基类的Init方法,避免隐藏 + using Component::Init; + void Init(std::string Str, TTF_Font *font, SDL_Color color); + void Init(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize); + void HandleEvents(SDL_Event *e) override; + void Update(float deltaTime) override; + void Render(float deltaTime) override; + void Clear() override; + + SDL_Texture *GetTexture(); + +public: + // 组件标签 + Tag m_tag = Tag::RENDER | Tag::UPDATE; // 标记该组件需要渲染和更新 + + std::string m_text; + TTF_Font *m_font; + SDL_Color m_text_color; + SDL_Color m_stroke_color; + int m_stroke_size = 0; + + SDL_Point Pos = {0, 0}; // 位置坐标 + SDL_Point TextureSize = {0, 0}; // 纹理大小 + SDL_Point Size = {0, 0}; // 大小 + SDL_Point Anchor = {0, 0}; // 中心点 + float Angle = 0.0f; // 旋转角度 + SDL_RendererFlip flip = SDL_FLIP_NONE; // 翻转 + +public: + // 设置坐标 + void SetPos(SDL_Point pos); + // 设置文本 + void SetText(std::string Str); + + // 获取坐标 + SDL_Point GetPos(); + // 获取文本 + std::string GetText(); +}; diff --git a/source/EngineFrame/Scene/Scene.cpp b/source/EngineFrame/Scene/Scene.cpp new file mode 100644 index 0000000..eb0f71a --- /dev/null +++ b/source/EngineFrame/Scene/Scene.cpp @@ -0,0 +1,42 @@ +#include "EngineFrame/Scene/Scene.h" +#include "Scene.h" + +void Scene::Enter() +{ +} + +void Scene::Exit() +{ + +} + +void Scene::AddChild(RefPtr actor) +{ + m_Actors.PushBack(actor); + actor->Parent = this; +} + +void Scene::Update(float deltaTime) +{ + RefPtr child = m_Actors.GetFirst(); + while (child) + { + child->Update(deltaTime); + child = child->GetNext(); + } +} + +void Scene::Render(float deltaTime) +{ + RefPtr child = m_Actors.GetFirst(); + while (child) + { + child->Render(deltaTime); + child = child->GetNext(); + } +} + +void Scene::HandleEvents(SDL_Event *e) +{ +} + diff --git a/source/EngineFrame/Scene/Scene.h b/source/EngineFrame/Scene/Scene.h new file mode 100644 index 0000000..c0831ab --- /dev/null +++ b/source/EngineFrame/Scene/Scene.h @@ -0,0 +1,22 @@ +#pragma once +#include "EngineFrame/Scene/Scene_Base.h" +#include "EngineFrame/Actor/Actor.h" +#include "Tool/IntrusiveList.hpp" +#include "Tool/RefPtr.h" +#include +#include +#include +class Scene : public Scene_Base, public RefObject +{ +public: + void Enter() override; + void HandleEvents(SDL_Event *e) override; + void Update(float deltaTime) override; + void Render(float deltaTime) override; + void Exit() override; + +public: + void AddChild(RefPtr actor); +private: + IntrusiveList> m_Actors; +}; \ No newline at end of file diff --git a/source/EngineFrame/Scene/Scene_Base.h b/source/EngineFrame/Scene/Scene_Base.h new file mode 100644 index 0000000..e69cb6d --- /dev/null +++ b/source/EngineFrame/Scene/Scene_Base.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include +class Scene_Base +{ +public: + Scene_Base() = default; + virtual ~Scene_Base() = default; + + virtual void Enter() = 0; + virtual void HandleEvents(SDL_Event *e) = 0; + virtual void Update(float deltaTime) = 0; + virtual void Render(float deltaTime) = 0; + virtual void Exit() = 0; +}; \ No newline at end of file diff --git a/source/Tool/Allocator.cpp b/source/Tool/Allocator.cpp new file mode 100644 index 0000000..4848b48 --- /dev/null +++ b/source/Tool/Allocator.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "Tool/Allocator.h" + +namespace memory +{ + MemoryAllocator *current_allocator_ = nullptr; + + MemoryAllocator *GetGlobalAllocator() + { + class GlobalAllocator : public MemoryAllocator + { + public: + virtual void *Alloc(size_t size) override + { + return ::operator new(size); + } + + virtual void Free(void *ptr) override + { + ::operator delete(ptr); + } + }; + + static GlobalAllocator global_allocator; + return &global_allocator; + } + + MemoryAllocator *GetAllocator() + { + if (!current_allocator_) + { + current_allocator_ = GetGlobalAllocator(); + } + return current_allocator_; + } + + void SetAllocator(MemoryAllocator *allocator) + { + current_allocator_ = allocator; + } + +} // namespace memory \ No newline at end of file diff --git a/source/Tool/Allocator.h b/source/Tool/Allocator.h new file mode 100644 index 0000000..c49909e --- /dev/null +++ b/source/Tool/Allocator.h @@ -0,0 +1,151 @@ +#pragma once +#include +#include +#include +namespace memory +{ + /// \~chinese + /// @brief 内存分配器 + class MemoryAllocator + { + public: + /// \~chinese + /// @brief 申请内存 + virtual void *Alloc(size_t size) = 0; + + /// \~chinese + /// @brief 释放内存 + virtual void Free(void *ptr) = 0; + }; + + /// \~chinese + /// @brief 获取当前内存分配器 + MemoryAllocator *GetAllocator(); + + /// \~chinese + /// @brief 设置当前内存分配器 + void SetAllocator(MemoryAllocator *allocator); + + /// \~chinese + /// @brief 使用当前内存分配器分配内存 + inline void *Alloc(size_t size) + { + return memory::GetAllocator()->Alloc(size); + } + + /// \~chinese + /// @brief 使用当前内存分配器释放内存 + inline void Free(void *ptr) + { + memory::GetAllocator()->Free(ptr); + } + +} // namespace memory + +/// \~chinese +/// @brief 分配器 +template +class Allocator +{ +public: + typedef _Ty value_type; + typedef _Ty *pointer; + typedef const _Ty *const_pointer; + typedef _Ty &reference; + typedef const _Ty &const_reference; + + using size_type = size_t; + using difference_type = ptrdiff_t; + + template + struct rebind + { + using other = Allocator<_Other>; + }; + + Allocator() noexcept {} + + Allocator(const Allocator &) noexcept = default; + + template + Allocator(const Allocator<_Other> &) noexcept + { + } + + inline _Ty *allocate(size_t count) + { + if (count > 0) + { + return static_cast<_Ty *>(memory::Alloc(sizeof(_Ty) * count)); + } + return nullptr; + } + + inline void *allocate(size_t count, const void *) + { + return allocate(count); + } + + inline void deallocate(void *ptr, size_t count) + { + memory::Free(ptr /*, sizeof(_Ty) * count */); + } + + template + inline void construct(_UTy *const ptr, _Args &&...args) + { + ::new (const_cast(static_cast(ptr))) _Ty(std::forward<_Args>(args)...); + } + + template + inline void destroy(_UTy *ptr) + { + ptr->~_UTy(); + } + + size_t max_size() const noexcept + { + return std::numeric_limits::max() / sizeof(_Ty); + } + + _Ty *address(_Ty &val) const noexcept + { + return std::addressof(val); + } + + const _Ty *address(const _Ty &val) const noexcept + { + return std::addressof(val); + } +}; + +// Allocator +template <> +class Allocator +{ +public: + using value_type = void; + typedef void *pointer; + typedef const void *const_pointer; + + using size_type = size_t; + using difference_type = ptrdiff_t; + + template + struct rebind + { + using other = Allocator<_Other>; + }; +}; + +template +bool operator==(const Allocator<_Ty> &, const Allocator<_Other> &) noexcept +{ + return true; +} + +template +bool operator!=(const Allocator<_Ty> &, const Allocator<_Other> &) noexcept +{ + return false; +} \ No newline at end of file diff --git a/source/Tool/Blob.hpp b/source/Tool/Blob.hpp new file mode 100644 index 0000000..296c3e6 --- /dev/null +++ b/source/Tool/Blob.hpp @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include +#include + +class Blob +{ + using BYTE = unsigned char; + +private: + const std::vector &m_blob; + size_t m_offset; + + // 检查是否有足够的剩余字节 + void checkSize(size_t required) const + { + if (m_offset + required > m_blob.size()) + { + throw std::out_of_range("Insufficient data in blob"); + } + } + +public: + // 构造函数,接受一个字节向量的引用 + Blob(const std::vector &blob) : m_blob(blob), m_offset(0) {} + + // 重置解析偏移量 + void reset() { m_offset = 0; } + + // 获取当前偏移量 + size_t getOffset() const { return m_offset; } + + // 设置偏移量 + void setOffset(size_t offset) + { + if (offset > m_blob.size()) + { + throw std::out_of_range("Offset out of range"); + } + m_offset = offset; + } + + // 获取剩余字节数 + size_t remaining() const { return m_blob.size() - m_offset; } + + // 读取一个T类型的数据(适用于基本类型) + template + T get() + { + checkSize(sizeof(T)); + + T value; + std::copy(m_blob.begin() + m_offset, + m_blob.begin() + m_offset + sizeof(T), + reinterpret_cast(&value)); + m_offset += sizeof(T); + + return value; + } + + // 读取int类型 + int32_t getInt() { return get(); } + + // 读取无符号int类型 + uint32_t getUInt() { return get(); } + + // 读取short类型 + int16_t getShort() { return get(); } + + // 读取无符号short类型 + uint16_t getUShort() { return get(); } + + // 读取float类型 + float getFloat() { return get(); } + + // 读取double类型 + double getDouble() { return get(); } + + BYTE getByte() { return get(); } + + float get256() + { + BYTE buf = get(); + return static_cast(buf); + } + + // 读取指定长度的字符串 + std::string getString(size_t length) + { + checkSize(length); + + std::string str(reinterpret_cast(m_blob.data() + m_offset), length); + m_offset += length; + + return str; + } + + // 读取以null结尾的字符串 + std::string getNullTerminatedString() + { + size_t length = 0; + size_t pos = m_offset; + + // 查找null终止符 + while (pos < m_blob.size() && m_blob[pos] != '\0') + { + pos++; + length++; + } + + // 确保找到了终止符 + if (pos >= m_blob.size()) + { + throw std::runtime_error("Null-terminated string not found"); + } + + // 读取字符串(不包含终止符) + std::string str(reinterpret_cast(m_blob.data() + m_offset), length); + m_offset = pos + 1; // 跳过终止符 + + return str; + } + + // 读取指定数量的字节 + std::vector getBytes(size_t count) + { + checkSize(count); + + std::vector bytes(m_blob.begin() + m_offset, m_blob.begin() + m_offset + count); + m_offset += count; + + return bytes; + } +}; diff --git a/source/Tool/Common.h b/source/Tool/Common.h new file mode 100644 index 0000000..0398401 --- /dev/null +++ b/source/Tool/Common.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// \~chinese +/// @brief 不可拷贝对象 +class Noncopyable +{ +protected: + Noncopyable() = default; + +private: + Noncopyable(const Noncopyable &) = delete; + + Noncopyable &operator=(const Noncopyable &) = delete; +}; diff --git a/source/Tool/Ifstream_NPK.cpp b/source/Tool/Ifstream_NPK.cpp new file mode 100644 index 0000000..1734543 --- /dev/null +++ b/source/Tool/Ifstream_NPK.cpp @@ -0,0 +1,86 @@ +#include "Ifstream_NPK.h" + +// 密钥初始化 +Ifstream_NPK::Ifstream_NPK() : Key{112, 117, 99, 104, 105, 107, 111, 110, 64, 110, 101, 111, 112, 108, 101, 32, 100, 117, 110, 103, 101, 111, 110, 32, 97, 110, 100, 32, 102, 105, 103, 104, 116, 101, 114, 32, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 0} +{ + // 构造函数体可以为空 +} + +// char* 转整数 +int Ifstream_NPK::CharToInt(char *Str) +{ + return *(int *)Str; +} + +// char* 转Long +long Ifstream_NPK::CharToLong(char *Str) +{ + return *(long long *)Str; +} + +// 读整数 +int Ifstream_NPK::ReadInt() +{ + char *CountBuffer = new char[4]; + for (int i = 0; i < 4; i++) + { + this->get(CountBuffer[i]); + } + int Count = CharToInt(CountBuffer); + delete[] CountBuffer; + return Count; +} + +// 读字符串 +std::string Ifstream_NPK::ReadString() +{ + char *CharBuffer = new char[1024]; + this->get(CharBuffer, 1024, '\0'); + std::string Str = CharBuffer; + delete[] CharBuffer; + this->seekg(1, std::ios::cur); + return Str; +} + +// 读取信息 +std::string Ifstream_NPK::ReadInfo() +{ + char *CharBuffer = new char[256]; + char var; + int i = 0; + while (i < 256) + { + this->get(var); + CharBuffer[i] = var ^ Key[i]; + ++i; + } + std::string Str = CharBuffer; + delete[] CharBuffer; + return Str; +} + +// 读LONG +int Ifstream_NPK::ReadLong() +{ + char *CountBuffer = new char[8]; + for (int i = 0; i < 8; i++) + { + this->get(CountBuffer[i]); + } + long Count = CharToLong(CountBuffer); + delete[] CountBuffer; + return Count; +} + +// 读指定长度数据 +BYTE *Ifstream_NPK::ReadCustomSize(int Size) +{ + BYTE *CharBuffer = new BYTE[Size]; + for (int j = 0; j < Size; j++) + { + char var; + this->get(var); + CharBuffer[j] = var; + } + return CharBuffer; +} diff --git a/source/Tool/Ifstream_NPK.h b/source/Tool/Ifstream_NPK.h new file mode 100644 index 0000000..8380387 --- /dev/null +++ b/source/Tool/Ifstream_NPK.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +typedef unsigned char BYTE; + +class Ifstream_NPK : public std::ifstream +{ +private: + const BYTE Key[256]; + +public: + // 构造函数 + Ifstream_NPK(); + + // char* 转整数 + int CharToInt(char *Str); + + // char* 转Long + long CharToLong(char *Str); + + // 读整数 + int ReadInt(); + + // 读字符串 + std::string ReadString(); + + // 读取信息 + std::string ReadInfo(); + + // 读LONG + int ReadLong(); + + // 读指定长度数据 + BYTE *ReadCustomSize(int Size); +}; diff --git a/source/Tool/Ifstream_PVF.cpp b/source/Tool/Ifstream_PVF.cpp new file mode 100644 index 0000000..1e95779 --- /dev/null +++ b/source/Tool/Ifstream_PVF.cpp @@ -0,0 +1,38 @@ +#include "Ifstream_PVF.h" + +Ifstream_PVF::Ifstream_PVF(std::string fileName) +{ + std::ifstream file(fileName, std::ios::binary | std::ios::in); + if (file.is_open()) + { + // 获取文件大小 + file.seekg(0, std::ios::end); + std::streamsize length = file.tellg(); + file.seekg(0, std::ios::beg); + + if (length > 0) + { + // 直接调整vector大小并读取数据 + _Data.resize(static_cast(length)); + if (file.read(_Data.data(), length)) + { + SDL_Log("文件读取成功,大小: %ld bytes", length); + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "文件读取不完整"); + _Data.clear(); + } + } + file.close(); + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "无法打开文件: %s", fileName.c_str()); + } +} + +Ifstream_PVF::~Ifstream_PVF() +{ + // vector会自动释放内存,无需手动操作 +} diff --git a/source/Tool/Ifstream_PVF.h b/source/Tool/Ifstream_PVF.h new file mode 100644 index 0000000..c50dc05 --- /dev/null +++ b/source/Tool/Ifstream_PVF.h @@ -0,0 +1,169 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +typedef unsigned char BYTE; + +// 定义宏用于注册获取不同类型值的函数 +#define REGISTER_GET_FUNCTION(Type, FunctionName) \ + Type Get##FunctionName() \ + { \ + char buffer[sizeof(Type)]; \ + read(buffer, sizeof(Type)); \ + Type result; \ + std::memcpy(&result, buffer, sizeof(Type)); \ + return result; \ + } +class Ifstream_PVF +{ +public: + // 使用vector存储数据,替代char* + std::vector _Data; + // 当前位置 + int _CurPos = 0; + // 上一次读取的实际大小 + int _LastReadSize = 0; + +public: + Ifstream_PVF(std::string fileName); + ~Ifstream_PVF(); + +public: + int tellg() + { + return _CurPos; + } + + // 获取数据大小 + int size() const + { + return static_cast(_Data.size()); + } + + void read(char *ptr, int size) + { + // 使用vector的size()作为最大长度 + if ((size + _CurPos) > static_cast(_Data.size())) + { + size = static_cast(_Data.size()) - _CurPos; + } + memcpy(ptr, _Data.data() + _CurPos, size); + _CurPos += size; + _LastReadSize = size; + } + + int gcount() + { + return _LastReadSize; + } + + void seek(int _jidx) + { + // 确保不会越界 + _CurPos = std::clamp(_jidx, 0, static_cast(_Data.size())); + } + +public: + unsigned int charPtrToInt(const char *bytes) + { + unsigned int result; + std::memcpy(&result, bytes, sizeof(int)); + return result; + } + + void CrcDecode(const int Length, const int crc32) + { + int num = 0x81A79011; + int originalPos = tellg(); // 保存初始位置 + for (int i = 0; i < Length; i += 4) + { + int Pos = tellg(); + char buffer[4]; + read(buffer, 4); + unsigned int anInt = charPtrToInt(buffer); + unsigned int val = (anInt ^ num ^ crc32); + unsigned int jiemi = (val >> 6) | ((val << (32 - 6)) & 0xFFFFFFFF); + + // 使用vector的data()获取数据指针 + if (Pos + 3 < static_cast(_Data.size())) + { + _Data[Pos] = ((jiemi >> 0) & 0xFF); + _Data[Pos + 1] = ((jiemi >> 8) & 0xFF); + _Data[Pos + 2] = ((jiemi >> 16) & 0xFF); + _Data[Pos + 3] = ((jiemi >> 24) & 0xFF); + } + } + // 重置读取位置,因为解码过程中移动了指针 + seek(originalPos + Length); // 移动到解码后的位置 + } + + std::string tolower(std::string str) + { + for (size_t i = 0; i < str.length(); ++i) + { + str[i] = std::tolower(str[i]); + } + return str; + } + + std::vector split(const std::string &str, const std::string &delimiter) + { + std::vector tokens; + size_t pos = 0; + size_t found; + while ((found = str.find(delimiter, pos)) != std::string::npos) + { + tokens.push_back(str.substr(pos, found - pos)); + pos = found + delimiter.length(); + } + tokens.push_back(str.substr(pos)); + return tokens; + } + +public: + REGISTER_GET_FUNCTION(int, Int); + REGISTER_GET_FUNCTION(short, Short); + REGISTER_GET_FUNCTION(unsigned short, UShort); + + std::string GetString(const int size) + { + if (size <= 0) + return ""; + + // 确保不会读取超出范围的数据 + int readSize = std::min(size, static_cast(_Data.size()) - _CurPos); + std::string result(_Data.data() + _CurPos, readSize); + _CurPos += readSize; + _LastReadSize = readSize; + + if (readSize != size) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "未能成功读取指定字节数的数据!"); + } + + return result; + } + + std::string GetStringNormal(const int size) + { + // 与GetString实现相同,可根据实际需求区分 + if (size <= 0) + return ""; + + int readSize = std::min(size, static_cast(_Data.size()) - _CurPos); + std::string result(_Data.data() + _CurPos, readSize); + _CurPos += readSize; + _LastReadSize = readSize; + + if (readSize != size) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "未能成功读取指定字节数的数据!"); + } + + return result; + } +}; diff --git a/source/Tool/IntrusiveList.hpp b/source/Tool/IntrusiveList.hpp new file mode 100644 index 0000000..f4508e5 --- /dev/null +++ b/source/Tool/IntrusiveList.hpp @@ -0,0 +1,462 @@ +#pragma once +#include +#include +#include + +/// \~chinese +/// @brief 侵入式链表 +template +class IntrusiveList +{ +public: + using value_type = typename std::pointer_traits<_PtrTy>::pointer; + using pointer = value_type *; + using reference = value_type &; + + IntrusiveList() + : first_(), last_() + { + } + + ~IntrusiveList() + { + Clear(); + } + + /// \~chinese + /// @brief 获取首元素 + const value_type &GetFirst() const + { + return first_; + } + + /// \~chinese + /// @brief 获取首元素 + value_type &GetFirst() + { + return first_; + } + + /// \~chinese + /// @brief 获取尾元素 + const value_type &GetLast() const + { + return last_; + } + + /// \~chinese + /// @brief 获取尾元素 + value_type &GetLast() + { + return last_; + } + + /// \~chinese + /// @brief 链表是否为空 + inline bool IsEmpty() const + { + return first_ == nullptr; + } + + /// \~chinese + /// @brief 在链表尾部添加对象 + void PushBack(reference child) + { + if (child->GetPrev()) + child->GetPrev()->GetNext() = child->GetNext(); + if (child->GetNext()) + child->GetNext()->GetPrev() = child->GetPrev(); + + child->GetPrev() = last_; + child->GetNext() = nullptr; + + if (first_) + { + last_->GetNext() = child; + } + else + { + first_ = child; + } + + last_ = child; + } + + /// \~chinese + /// @brief 在链表头部添加对象 + void PushFront(reference child) + { + if (child->GetPrev()) + child->GetPrev()->GetNext() = child->GetNext(); + if (child->GetNext()) + child->GetNext()->GetPrev() = child->GetPrev(); + + child->GetPrev() = nullptr; + child->GetNext() = first_; + + if (first_) + { + first_->GetPrev() = child; + } + else + { + last_ = child; + } + + first_ = child; + } + + /// \~chinese + /// @brief 在链表的对象前插入新对象 + void InsertBefore(reference child, reference before) + { + if (child->GetPrev()) + child->GetPrev()->GetNext() = child->GetNext(); + if (child->GetNext()) + child->GetNext()->GetPrev() = child->GetPrev(); + + if (before->GetPrev()) + before->GetPrev()->GetNext() = child; + else + first_ = child; + + child->GetPrev() = before->GetPrev(); + child->GetNext() = before; + before->GetPrev() = child; + } + + /// \~chinese + /// @brief 在链表的对象后插入新对象 + void InsertAfter(reference child, reference after) + { + if (child->GetPrev()) + child->GetPrev()->GetNext() = child->GetNext(); + if (child->GetNext()) + child->GetNext()->GetPrev() = child->GetPrev(); + + if (after->GetNext()) + after->GetNext()->GetPrev() = child; + else + last_ = child; + + child->GetNext() = after->GetNext(); + child->GetPrev() = after; + after->GetNext() = child; + } + + /// \~chinese + /// @brief 移除对象 + void Remove(reference child) + { + if (child->GetNext()) + { + child->GetNext()->GetPrev() = child->GetPrev(); + } + else + { + last_ = child->GetPrev(); + } + + if (child->GetPrev()) + { + child->GetPrev()->GetNext() = child->GetNext(); + } + else + { + first_ = child->GetNext(); + } + + child->GetPrev() = nullptr; + child->GetNext() = nullptr; + } + + /// \~chinese + /// @brief 清空所有对象 + void Clear() + { + value_type p = first_; + while (p) + { + value_type tmp = p; + p = p->GetNext(); + if (tmp) + { + tmp->GetNext() = nullptr; + tmp->GetPrev() = nullptr; + } + } + first_ = nullptr; + last_ = nullptr; + } + + /// \~chinese + /// @brief 检查链表是否有效 + bool CheckValid() + { + if (!first_) + return true; + + int pos = 0; + + value_type p = first_; + value_type tmp = p; + do + { + tmp = p; + p = p->GetNext(); + ++pos; + + if (p) + { + if (p->GetPrev() != tmp) + return false; + } + else + { + if (tmp != last_) + return false; + } + } while (p); + return true; + } + +public: + template + struct Iterator + { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = _IterPtrTy; + using pointer = _IterPtrTy *; + using reference = _IterPtrTy &; + using difference_type = ptrdiff_t; + + inline Iterator(value_type ptr = nullptr, bool is_end = false) + : base_(ptr), is_end_(is_end) + { + } + + inline reference operator*() const + { + KGE_ASSERT(base_ && !is_end_); + return const_cast(base_); + } + + inline pointer operator->() const + { + return std::pointer_traits::pointer_to(**this); + } + + inline Iterator &operator++() + { + KGE_ASSERT(base_ && !is_end_); + value_type next = base_->GetNext(); + if (next) + base_ = next; + else + is_end_ = true; + return (*this); + } + + inline Iterator operator++(int) + { + Iterator old = (*this); + ++(*this); + return old; + } + + inline Iterator &operator--() + { + KGE_ASSERT(base_); + if (is_end_) + is_end_ = false; + else + base_ = base_->GetPrev(); + return (*this); + } + + inline Iterator operator--(int) + { + Iterator old = (*this); + --(*this); + return old; + } + + inline bool operator==(const Iterator &other) const + { + return base_ == other.base_ && is_end_ == other.is_end_; + } + + inline bool operator!=(const Iterator &other) const + { + return !(*this == other); + } + + inline operator bool() const + { + return base_ != nullptr && !is_end_; + } + + private: + bool is_end_; + + typename std::remove_const::type base_; + }; + +public: + using iterator = Iterator; + using const_iterator = Iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + inline iterator begin() + { + return iterator(first_, first_ == nullptr); + } + + inline const_iterator begin() const + { + return const_iterator(first_, first_ == nullptr); + } + + inline const_iterator cbegin() const + { + return begin(); + } + + inline iterator end() + { + return iterator(last_, true); + } + + inline const_iterator end() const + { + return const_iterator(last_, true); + } + + inline const_iterator cend() const + { + return end(); + } + + inline reverse_iterator rbegin() + { + return reverse_iterator(end()); + } + + inline const_reverse_iterator rbegin() const + { + return const_reverse_iterator(end()); + } + + inline const_reverse_iterator crbegin() const + { + return rbegin(); + } + + inline reverse_iterator rend() + { + return reverse_iterator(begin()); + } + + inline const_reverse_iterator rend() const + { + return const_reverse_iterator(begin()); + } + + inline const_reverse_iterator crend() const + { + return rend(); + } + + inline value_type &front() + { + if (IsEmpty()) + throw std::out_of_range("front() called on empty list"); + return first_; + } + + inline const value_type &front() const + { + if (IsEmpty()) + throw std::out_of_range("front() called on empty list"); + return first_; + } + + inline value_type &back() + { + if (IsEmpty()) + throw std::out_of_range("back() called on empty list"); + return last_; + } + + inline const value_type &back() const + { + if (IsEmpty()) + throw std::out_of_range("back() called on empty list"); + return last_; + } + +private: + value_type first_; + value_type last_; +}; + +/// \~chinese +/// @brief 侵入式链表元素 +template +class IntrusiveListValue +{ +public: + using value_type = typename std::pointer_traits<_PtrTy>::pointer; + using reference = value_type &; + using pointer = value_type *; + + IntrusiveListValue() + : prev_(nullptr), next_(nullptr) + { + } + + IntrusiveListValue(value_type rhs) + : prev_(nullptr), next_(nullptr) + { + if (rhs) + { + prev_ = rhs->GetPrev(); + next_ = rhs->GetNext(); + } + } + + /// \~chinese + /// @brief 获取前一元素 + const value_type &GetPrev() const + { + return prev_; + } + + /// \~chinese + /// @brief 获取前一元素 + value_type &GetPrev() + { + return prev_; + } + + /// \~chinese + /// @brief 获取下一元素 + const value_type &GetNext() const + { + return next_; + } + + /// \~chinese + /// @brief 获取下一元素 + value_type &GetNext() + { + return next_; + } + +private: + value_type prev_; + value_type next_; + + friend class IntrusiveList<_PtrTy>; +}; diff --git a/source/Tool/RefBasePtr.hpp b/source/Tool/RefBasePtr.hpp new file mode 100644 index 0000000..272e907 --- /dev/null +++ b/source/Tool/RefBasePtr.hpp @@ -0,0 +1,257 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include +#include +#include "Tool/Common.h" + +/** + * \~chinese + * @brief 引用计数智能指针 + */ +template +class RefBasePtr : protected _RefPolicy +{ +public: + using value_type = _Ty; + using pointer_type = _Ty *; + using const_pointer_type = const _Ty *; + using reference_type = _Ty &; + using const_reference_type = const _Ty &; + + RefBasePtr() noexcept + : ptr_(nullptr) + { + } + + RefBasePtr(std::nullptr_t) noexcept + : ptr_(nullptr) + { + } + + RefBasePtr(pointer_type p) + : ptr_(p) + { + _RefPolicy::Retain(ptr_); + } + + RefBasePtr(const RefBasePtr &other) + : ptr_(other.ptr_) + { + _RefPolicy::Retain(ptr_); + } + + RefBasePtr(RefBasePtr &&other) noexcept + : ptr_(nullptr) + { + Swap(other); + } + + ~RefBasePtr() + { + Tidy(); + } + + template ::value, int>::type = 0> + RefBasePtr(const RefBasePtr<_UTy, _RefPolicy> &other) + { + ptr_ = dynamic_cast(other.Get()); + _RefPolicy::Retain(ptr_); + } + + inline pointer_type Get() const noexcept + { + return ptr_; + } + + inline pointer_type *GetAddressOfAndRelease() + { + Tidy(); + return &ptr_; + } + + inline void Reset(pointer_type ptr = nullptr) + { + if (ptr) + RefBasePtr(ptr).Swap(*this); + else + Tidy(); + } + + inline void Swap(RefBasePtr &other) noexcept + { + std::swap(ptr_, other.ptr_); + } + + inline pointer_type operator->() + { + return ptr_; + } + + inline const_pointer_type operator->() const + { + return ptr_; + } + + inline reference_type operator*() + { + return *ptr_; + } + + inline const_reference_type operator*() const + { + return *ptr_; + } + + inline pointer_type *operator&() + { + return this->GetAddressOfAndRelease(); + } + + inline operator bool() const noexcept + { + return ptr_ != nullptr; + } + + inline bool operator!() const noexcept + { + return ptr_ == 0; + } + + inline RefBasePtr &operator=(const RefBasePtr &other) + { + if (other.ptr_ != ptr_) + RefBasePtr(other).Swap(*this); + return (*this); + } + + inline RefBasePtr &operator=(RefBasePtr &&other) noexcept + { + if (other.ptr_ != ptr_) + other.Swap(*this); + return (*this); + } + + inline RefBasePtr &operator=(pointer_type p) + { + if (p != ptr_) + RefBasePtr(p).Swap(*this); + return (*this); + } + + template ::value, int>::type = 0> + inline RefBasePtr &operator=(const RefBasePtr<_UTy, _RefPolicy> &other) + { + if (other.Get() != ptr_) + RefBasePtr(dynamic_cast(other.Get())).Swap(*this); + return (*this); + } + + inline RefBasePtr &operator=(std::nullptr_t) + { + Tidy(); + return *this; + } + +private: + void Tidy() + { + _RefPolicy::Release(ptr_); + ptr_ = nullptr; + } + +private: + pointer_type ptr_; +}; + +template +inline bool operator==(const RefBasePtr<_Ty, _RefPolicy> &lhs, const RefBasePtr<_UTy, _RefPolicy> &rhs) noexcept +{ + return lhs.Get() == rhs.Get(); +} + +template +inline bool operator==(const RefBasePtr<_Ty, _RefPolicy> &lhs, _Ty *rhs) noexcept +{ + return lhs.Get() == rhs; +} + +template +inline bool operator==(_Ty *lhs, const RefBasePtr<_Ty, _RefPolicy> &rhs) noexcept +{ + return lhs == rhs.Get(); +} + +template +inline bool operator==(const RefBasePtr<_Ty, _RefPolicy> &lhs, std::nullptr_t) noexcept +{ + return !static_cast(lhs); +} + +template +inline bool operator==(std::nullptr_t, const RefBasePtr<_Ty, _RefPolicy> &rhs) noexcept +{ + return !static_cast(rhs); +} + +template +inline bool operator!=(const RefBasePtr<_Ty, _RefPolicy> &lhs, const RefBasePtr<_UTy, _RefPolicy> &rhs) noexcept +{ + return !(lhs == rhs); +} + +template +inline bool operator!=(const RefBasePtr<_Ty, _RefPolicy> &lhs, _Ty *rhs) noexcept +{ + return lhs.Get() != rhs; +} + +template +inline bool operator!=(_Ty *lhs, const RefBasePtr<_Ty, _RefPolicy> &rhs) noexcept +{ + return lhs != rhs.Get(); +} + +template +inline bool operator!=(const RefBasePtr<_Ty, _RefPolicy> &lhs, std::nullptr_t) noexcept +{ + return static_cast(lhs); +} + +template +inline bool operator!=(std::nullptr_t, const RefBasePtr<_Ty, _RefPolicy> &rhs) noexcept +{ + return static_cast(rhs); +} + +template +inline bool operator<(const RefBasePtr<_Ty, _RefPolicy> &lhs, const RefBasePtr<_UTy, _RefPolicy> &rhs) noexcept +{ + return lhs.Get() < rhs.Get(); +} + +// template class cannot specialize std::swap, +// so implement a swap function in kiwano namespace +template +inline void swap(RefBasePtr<_Ty, _RefPolicy> &lhs, RefBasePtr<_Ty, _RefPolicy> &rhs) noexcept +{ + lhs.Swap(rhs); +} diff --git a/source/Tool/RefObject.cpp b/source/Tool/RefObject.cpp new file mode 100644 index 0000000..1a5f83a --- /dev/null +++ b/source/Tool/RefObject.cpp @@ -0,0 +1,86 @@ +#include "Tool/RefObject.h" + +RefObject::RefObject() + : ref_count_(0) // 修正:新创建对象引用计数应为1 +{ +} + +RefObject::~RefObject() {} + +void RefObject::Retain() +{ + // 修正:使用fetch_add确保原子操作 + ref_count_.fetch_add(1, std::memory_order_relaxed); +} + +void RefObject::Release() +{ + // 修正:使用fetch_sub确保原子操作并检查结果 + if (ref_count_.fetch_sub(1, std::memory_order_acq_rel) == 1) + { + delete this; + } +} + +uint32_t RefObject::GetRefCount() const +{ + return ref_count_.load(std::memory_order_relaxed); +} + +void *RefObject::operator new(size_t size) +{ + void *ptr = memory::Alloc(size); + if (!ptr) + { + throw std::bad_alloc(); + } + return ptr; +} + +void RefObject::operator delete(void *ptr) +{ + if (ptr) // 增加空指针检查 + { + memory::Free(ptr); + } +} + +// 修正:添加noexcept说明符,与声明一致 +void *RefObject::operator new(size_t size, std::nothrow_t const &) noexcept +{ + try + { + return memory::Alloc(size); + } + catch (...) + { + return nullptr; + } +} + +// 修正:添加noexcept说明符,与声明一致 +void RefObject::operator delete(void *ptr, std::nothrow_t const &) noexcept +{ + if (ptr) // 增加空指针检查 + { + try + { + memory::Free(ptr); + } + catch (...) + { + // 忽略异常,符合noexcept语义 + } + } +} + +// 修正:添加noexcept说明符,与声明一致 +void *RefObject::operator new(size_t size, void *ptr) noexcept +{ + return ::operator new(size, ptr); +} + +void RefObject::operator delete(void *ptr, void *place) noexcept +{ + ::operator delete(ptr, place); +} diff --git a/source/Tool/RefObject.h b/source/Tool/RefObject.h new file mode 100644 index 0000000..047f7f4 --- /dev/null +++ b/source/Tool/RefObject.h @@ -0,0 +1,45 @@ +#pragma once + +#include "Tool/Common.h" +#include "Tool/Allocator.h" +#include + +/** + * \~chinese + * @brief 引用计数器 + */ +class RefObject : protected Noncopyable +{ +public: + /// \~chinese + /// @brief 增加引用计数 + void Retain(); + + /// \~chinese + /// @brief 减少引用计数 + void Release(); + + /// \~chinese + /// @brief 获取引用计数 + uint32_t GetRefCount() const; + + static void *operator new(size_t size); + + static void operator delete(void *ptr); + + static void *operator new(size_t size, std::nothrow_t const &) noexcept; + + static void operator delete(void *ptr, std::nothrow_t const &) noexcept; + + static void *operator new(size_t size, void *ptr) noexcept; + + static void operator delete(void *ptr, void *place) noexcept; + + virtual ~RefObject(); + +protected: + RefObject(); + +private: + std::atomic ref_count_; +}; diff --git a/source/Tool/RefPtr.h b/source/Tool/RefPtr.h new file mode 100644 index 0000000..fa807f6 --- /dev/null +++ b/source/Tool/RefPtr.h @@ -0,0 +1,63 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include "Tool/RefObject.h" +#include "Tool/RefBasePtr.hpp" + +/// \~chinese +/// @brief 默认的智能指针引用计数策略 +struct DefaultRefPtrPolicy +{ + inline void Retain(RefObject *ptr) + { + if (ptr) + ptr->Retain(); + } + + inline void Release(RefObject *ptr) + { + if (ptr) + ptr->Release(); + } +}; + +/// \~chinese +/// @brief 引用计数对象智能指针 +template +using RefPtr = RefBasePtr<_Ty, DefaultRefPtrPolicy>; + +/// \~chinese +/// @brief 构造引用计数对象智能指针 +template +inline RefPtr<_Ty> MakePtr(_Args &&...args) +{ + static_assert(std::is_base_of::value, "_Ty must be derived from RefObject"); + return RefPtr<_Ty>(new _Ty(std::forward<_Args>(args)...)); +} + +/// \~chinese +/// @brief 构造引用计数对象智能指针 +template +inline RefPtr<_Ty> MakePtr(_Ty *ptr) +{ + static_assert(std::is_base_of::value, "_Ty must be derived from RefObject"); + return RefPtr<_Ty>(ptr); +} diff --git a/source/Tool/RemoteLogger.cpp b/source/Tool/RemoteLogger.cpp new file mode 100644 index 0000000..385f8b7 --- /dev/null +++ b/source/Tool/RemoteLogger.cpp @@ -0,0 +1,94 @@ +#include "RemoteLogger.h" + +RemoteLogger::RemoteLogger() : sockfd(-1), is_initialized(false) +{ + memset(&server_addr, 0, sizeof(server_addr)); + server_len = sizeof(server_addr); +} + +RemoteLogger::~RemoteLogger() +{ + close(); +} + +bool RemoteLogger::Init(const char *target_ip, unsigned short target_port) +{ + // 如果已经初始化,先关闭 + if (is_initialized) + { + close(); + } + + // 创建UDP socket + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + return false; + } + + // 设置服务器地址 + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(target_port); + + // 转换IP地址 + if (inet_pton(AF_INET, target_ip, &server_addr.sin_addr) <= 0) + { + ::close(sockfd); + sockfd = -1; + return false; + } + + is_initialized = true; + return true; +} + +std::string RemoteLogger::format_string(const char *format, va_list vl) +{ + // 第一次调用:获取所需缓冲区大小 + va_list vl_copy; + va_copy(vl_copy, vl); // 复制 va_list(关键!避免原 vl 被修改) + int length = vsnprintf(nullptr, 0, format, vl_copy); + va_end(vl_copy); // 释放复制的 va_list + + if (length < 0) + { + return "Format error"; // 格式化失败时返回明确信息 + } + + // 第二次调用:实际格式化字符串 + std::vector buffer(length + 1); // 用 vector 自动管理内存 + vsnprintf(buffer.data(), buffer.size(), format, vl); + return std::string(buffer.data()); // 直接返回,无需手动移除空字符 +} +void RemoteLogger::log(const char *format, ...) +{ + va_list vl; + va_start(vl, format); + + std::string message = format_string(format, vl); + va_end(vl); + + // 先输出到SDL日志 + SDL_Log(message.c_str()); + if (!is_initialized || sockfd < 0) + return; + + // 通过UDP发送 + sendto(sockfd, message.c_str(), message.size(), 0, + (struct sockaddr *)&server_addr, server_len); +} + +void RemoteLogger::close() +{ + if (sockfd >= 0) + { + ::close(sockfd); + sockfd = -1; + } + is_initialized = false; +} +// 检查是否已初始化 +bool RemoteLogger::isInitialized() const +{ + return is_initialized; +} \ No newline at end of file diff --git a/source/Tool/RemoteLogger.h b/source/Tool/RemoteLogger.h new file mode 100644 index 0000000..8412058 --- /dev/null +++ b/source/Tool/RemoteLogger.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class RemoteLogger +{ +public: + RemoteLogger(const RemoteLogger &) = delete; + RemoteLogger &operator=(const RemoteLogger &) = delete; + RemoteLogger(RemoteLogger &&) = delete; + RemoteLogger &operator=(RemoteLogger &&) = delete; + // 全局访问点 + static RemoteLogger &GetInstance() + { + static RemoteLogger instance; // 局部静态变量,保证只初始化一次 + return instance; + } + // 安全格式化到字符串 + static std::string format_string(const char *format, va_list vl); + +public: + // 初始化函数,返回是否成功 + bool Init(const char *target_ip, unsigned short target_port); + // 日志输出函数,支持格式化字符串 + void log(const char *format, ...); + // 关闭连接 + void close(); + // 检查是否已初始化 + bool isInitialized() const; + +private: + RemoteLogger(/* args */); + ~RemoteLogger(); + + int sockfd = -1; + struct sockaddr_in server_addr; + socklen_t server_len; + bool is_initialized = false; +}; diff --git a/source/Tool/TagGed.h b/source/Tool/TagGed.h new file mode 100644 index 0000000..4121f37 --- /dev/null +++ b/source/Tool/TagGed.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include +enum class Tag +{ + NONE = 0, // 无标签 + UPDATE = 1 << 0, // 更新标签 + RENDER = 1 << 1, // 渲染标签 +}; + +constexpr Tag operator|(Tag a, Tag b) +{ + using Underlying = std::underlying_type_t; + return static_cast(static_cast(a) | static_cast(b)); +} + +constexpr Tag operator&(Tag a, Tag b) +{ + using Underlying = std::underlying_type_t; + return static_cast(static_cast(a) & static_cast(b)); +} +class TagGed +{ +private: + uint64_t m_tags; + +public: + TagGed(Tag initialTag = Tag::NONE) + : m_tags(static_cast(initialTag)) {} + + // 添加标签 + void addTag(Tag tag) + { + m_tags |= static_cast(tag); + } + + // 移除标签 + void removeTag(Tag tag) + { + m_tags &= ~static_cast(tag); + } + + // 判断是否包含指定标签 + bool hasTag(Tag tag) const + { + return (m_tags & static_cast(tag)) != 0; + } + + // 判断是否包含所有指定标签 + bool hasAllTags(Tag tags) const + { + return (m_tags & static_cast(tags)) == static_cast(tags); + } + + // 清除所有标签 + void clearTags() + { + m_tags = 0; + } +}; + diff --git a/source/Tool/ThreadPool.cpp b/source/Tool/ThreadPool.cpp new file mode 100644 index 0000000..e7cfbfe --- /dev/null +++ b/source/Tool/ThreadPool.cpp @@ -0,0 +1,96 @@ +#include "ThreadPool.h" +#include +#include + +// 获取单例实例 +ThreadPool &ThreadPool::GetInstance() +{ + static ThreadPool instance(3); // 固定3个工作线程 + return instance; +} + +ThreadPool::~ThreadPool() +{ + shutdown(); +} + +void ThreadPool::shutdown() +{ + printf("ThreadPool destroyed\n"); + { + std::unique_lock lock(queue_mutex); + stop = true; + // 清空所有线程的任务队列 + for (auto &queue : threadTasks) + { + // 使用swap技巧清空队列 + std::queue> empty; + std::swap(queue, empty); + } + } + + // 通知所有线程 + for (auto &cond : threadConditions) + cond->notify_all(); + + for (std::thread &worker : workers) + if (worker.joinable()) + worker.join(); + + // 释放条件变量 + for (auto &cond : threadConditions) + delete cond; +} + +// 获取线程池大小 +size_t ThreadPool::size() const +{ + return workers.size(); +} + +// 获取指定线程的负载(待处理任务数) +size_t ThreadPool::getThreadLoad(int threadId) const +{ + if (threadId < 0 || threadId >= static_cast(workers.size())) + throw std::runtime_error("Invalid thread ID"); + + std::unique_lock lock(queue_mutex); + return threadTasks.at(threadId).size(); +} + +// 私有构造函数实现 +ThreadPool::ThreadPool(size_t numThreads) : stop(false) +{ + // 为每个线程创建任务队列 + threadTasks.resize(numThreads); + + // 为每个线程创建条件变量 + for (size_t i = 0; i < numThreads; ++i) + { + threadConditions.push_back(new std::condition_variable()); + } + + for (size_t i = 0; i < numThreads; ++i) + { + workers.emplace_back([this, i] + { + while(true) { + std::function task; + + { + std::unique_lock lock(this->queue_mutex); + this->threadConditions[i]->wait(lock, [this, i] { + return this->stop || !this->threadTasks[i].empty(); + }); + + if(this->stop && this->threadTasks[i].empty()) + return; + + task = std::move(this->threadTasks[i].front()); + this->threadTasks[i].pop(); + } + + task(); + } }); + } +} diff --git a/source/Tool/ThreadPool.h b/source/Tool/ThreadPool.h new file mode 100644 index 0000000..680a122 --- /dev/null +++ b/source/Tool/ThreadPool.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ThreadPool +{ +public: + // 获取单例实例 + static ThreadPool &GetInstance(); + + // 删除拷贝构造函数和赋值运算符 + ThreadPool(const ThreadPool &) = delete; + ThreadPool &operator=(const ThreadPool &) = delete; + + // 向指定线程添加任务 + template + void enqueueToThread(int threadId, F &&f, Args &&...args) + { + if (threadId < 0 || threadId >= static_cast(workers.size())) + throw std::runtime_error("Invalid thread ID"); + + { + std::unique_lock lock(queue_mutex); + + // 不允许在停止后添加新任务 + if (stop) + throw std::runtime_error("enqueue on stopped ThreadPool"); + + // 将任务添加到指定线程的队列 + threadTasks[threadId].emplace([=]() mutable + { std::invoke(f, args...); }); + } + + // 通知指定线程 + threadConditions[threadId]->notify_one(); + } + + // 向任意线程添加任务(负载均衡) + template + void enqueue(F &&f, Args &&...args) + { + // 使用轮询方式选择线程 + static std::atomic nextThreadId(0); + int threadId = nextThreadId.load(); + nextThreadId = (nextThreadId + 1) % workers.size(); + + enqueueToThread(threadId, std::forward(f), std::forward(args)...); + } + + ~ThreadPool(); + + void shutdown(); + + // 获取线程池大小 + size_t size() const; + + // 获取指定线程的负载(待处理任务数) + size_t getThreadLoad(int threadId) const; + +private: + // 私有构造函数 + ThreadPool(size_t numThreads); + +private: + std::vector workers; + std::vector>> threadTasks; // 每个线程有自己的任务队列 + + mutable std::mutex queue_mutex; + std::vector threadConditions; // 使用指针存储条件变量 + std::atomic stop; +}; diff --git a/source/Tool/Tool_Network.cpp b/source/Tool/Tool_Network.cpp new file mode 100644 index 0000000..d702621 --- /dev/null +++ b/source/Tool/Tool_Network.cpp @@ -0,0 +1,86 @@ +#include "Tool/Tool_Network.h" + +// 定义内存管理结构体 +struct MemoryStruct +{ + char *memory; + size_t size; +}; + +size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + void *ptr = realloc(mem->memory, mem->size + realsize + 1); + if (!ptr) + { + RemoteLogger::GetInstance().log("内存分配失败 (out of memory)"); + return 0; + } + + mem->memory = (char *)ptr; + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = '\0'; + + return realsize; +} + +int http_get(const char *url, char **response, size_t *response_size) +{ + CURL *curl; + CURLcode res; + struct MemoryStruct chunk; + + // 初始化内存结构 + chunk.memory = (char *)malloc(1); + chunk.size = 0; + if (!chunk.memory) + { + RemoteLogger::GetInstance().log("初始内存分配失败"); + return -1; + } + + curl = curl_easy_init(); + if (curl) + { + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + + // 允许重定向 + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L); + + // 执行请求 + res = curl_easy_perform(curl); + + // 检查HTTP状态码 + long response_code; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + // RemoteLogger::GetInstance().log("HTTP 状态码: %ld"); + + if (res != CURLE_OK) + { + RemoteLogger::GetInstance().log("请求失败: %s", curl_easy_strerror(res)); + free(chunk.memory); + *response = NULL; + *response_size = 0; + curl_easy_cleanup(curl); + return -1; + } + + // 返回结果 + *response = chunk.memory; + *response_size = chunk.size; + + curl_easy_cleanup(curl); + return 0; + } + + free(chunk.memory); + *response = NULL; + *response_size = 0; + return -1; +} diff --git a/source/Tool/Tool_Network.h b/source/Tool/Tool_Network.h new file mode 100644 index 0000000..a125caa --- /dev/null +++ b/source/Tool/Tool_Network.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "Tool/RemoteLogger.h" + +// 回调函数 +size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp); + +// HTTP GET请求函数 +int http_get(const char *url, char **response, size_t *response_size); \ No newline at end of file diff --git a/source/Tool/Tool_String.hpp b/source/Tool/Tool_String.hpp new file mode 100644 index 0000000..b7945f5 --- /dev/null +++ b/source/Tool/Tool_String.hpp @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include + +std::string Tool_toLowerCase(const std::string &str) +{ + std::string result = str; + // 使用transform算法遍历字符串并转换为小写 + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) + { return std::tolower(c); }); + return result; +} diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..4ad4e23 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,84 @@ + +// 电脑的IP地址和端口 +#define PC_IP "192.168.200.3" +#define PC_SCRIPT_PORT "39018" +#define PC_LOG_PORT 39019 + +#include +#include +#include +#include +#include +#include +#include + +#include "EngineCore/Asset_ImagePack.h" +#include "EngineCore/Asset_Script.h" +#include "squirrel/SquirrelEx.h" +#include "Tool/RemoteLogger.h" +#include "EngineCore/Game.h" +#include "Scene/Scene_Loading_UI.h" +#include "Tool/RefPtr.h" +#include "Global/Global_Game.h" +#include "Tool/ThreadPool.h" + +void InitScript() +{ + SDL_Log("开始初始化PVF"); + // 初始化脚本资源系统 + Asset_Script::GetInstance().Init(); + SDL_Log("PVF初始化完成!"); +} + +void RunSetup() +{ + // 初始化Squirrel脚本系统 + SquirrelEx::GetInstance().Run(); + // 初始化全局游戏类 + Global_Game::GetInstance().Init(); + + // 设定UI层场景 + RefPtr sceneUI = new Scene_Loading_UI; + Game::GetInstance().ChangeUIScene(sceneUI); + + ThreadPool::GetInstance().enqueue(InitScript); +} + +int main(int argc, char *argv[]) +{ + socketInitializeDefault(); + nxlinkStdio(); // 启用nxlink调试输出 + curl_global_init(CURL_GLOBAL_DEFAULT); // 初始化libcurl + chdir("/switch/Lenheart/MyGame/"); + + // 初始化Image资源系统 + Asset_ImagePack::GetInstance().Init(); + // 初始化脚本资源系统 + Asset_Script::GetInstance(); + // 初始化线程池 + ThreadPool::GetInstance(); + + // 初始化日志系统 + if (RemoteLogger::GetInstance().Init(PC_IP, PC_LOG_PORT)) + { + RemoteLogger::GetInstance().log("日志系统初始化成功!\n"); + } + + // 初始化Squirrel脚本系统 + SquirrelEx::GetInstance().Init(); + // 请求脚本 + SquirrelEx::GetInstance().RequestNetScript(PC_IP, PC_SCRIPT_PORT); + + // 初始化游戏引擎 + Game &game = Game::GetInstance(); + // 初始化各项目 + game.Init(RunSetup); + // 进入游戏循环逻辑 + game.Run(); + game.Clear(); + + ThreadPool::GetInstance().shutdown(); + socketExit(); + curl_global_cleanup(); + return 0; +} diff --git a/source/squirrel/SquirrelEx.cpp b/source/squirrel/SquirrelEx.cpp new file mode 100644 index 0000000..aea3857 --- /dev/null +++ b/source/squirrel/SquirrelEx.cpp @@ -0,0 +1,121 @@ +#include "SquirrelEx.h" +#include "Tool/RemoteLogger.h" +#include "squirrel/sqr_sdl.hpp" +SquirrelEx::SquirrelEx() +{ +} +SquirrelEx::~SquirrelEx() +{ +} +void SquirrelEx::printfunc(HSQUIRRELVM v, const SQChar *s, ...) +{ + va_list vl; + va_start(vl, s); + std::string message = RemoteLogger::format_string(s, vl); + va_end(vl); + RemoteLogger::GetInstance().log(message.c_str()); +} + +void SquirrelEx::errorfunc(HSQUIRRELVM v, const SQChar *s, ...) +{ + va_list vl; + va_start(vl, s); + std::string message = RemoteLogger::format_string(s, vl); + va_end(vl); + RemoteLogger::GetInstance().log(message.c_str()); +} + +bool SquirrelEx::Compilebuffer(std::string Path, std::string Code) +{ + if (v == nullptr) + { + RemoteLogger::GetInstance().log("松鼠虚拟机未初始化!"); + return false; + } + if (sq_compilebuffer(v, Code.c_str(), Code.length(), Path.c_str(), SQTrue) >= 0) + { + sq_pushroottable(v); + sq_call(v, 1, SQFalse, SQTrue); + sq_pop(v, 1); + return true; + } + return false; +} + +void SquirrelEx::Init() +{ + v = sq_open(1024); // 栈大小1024 + sqstd_seterrorhandlers(v); + sq_pushroottable(v); + sqstd_register_bloblib(v); + sqstd_register_iolib(v); + sqstd_register_systemlib(v); + sqstd_register_mathlib(v); + sqstd_register_stringlib(v); + + sq_setprintfunc(v, printfunc, errorfunc); + RegisterSDLFunctions(v); +} + +void SquirrelEx::RequestNetScript(std::string Ip, std::string Port) +{ + char *response = NULL; + size_t response_size = 0; + std::string url = "http://" + Ip + ":" + Port + "/get/getadvertisement?key=Files"; // 确保协议正确 + int result = http_get(url.c_str(), &response, &response_size); + if (result == 0 && response != NULL && response_size > 0) + { + std::string response_str(response, response_size); + free(response); // 释放内存 + nlohmann::json ex1 = nlohmann::json::parse(response_str); + for (const auto &[key, value] : ex1.items()) + { + std::string key_str = key; + std::string value_str = value; + bool Flag = Compilebuffer(key_str, value_str); + if (!Flag) + { + RemoteLogger::GetInstance().log("Squirrel Compilebuffer Error! FileName: %s", key_str.c_str()); + } + } + } + else + { + if (response) + free(response); + } +} + +void SquirrelEx::Run() +{ + // 载入main函数 + SQInteger top = sq_gettop(v); // saves the stack size before the call + sq_pushroottable(v); // pushes the global table + sq_pushstring(v, _SC("main"), -1); + if (SQ_SUCCEEDED(sq_get(v, -2))) + { // gets the field 'foo' from the global table + sq_pushroottable(v); // push the 'this' (in this case is the global table) + sq_call(v, 1, SQFalse, SQTrue); // calls the function + } + sq_settop(v, top); // restores the original stack size +} + +void SquirrelEx::UiRender(float deltaTime) +{ + SQInteger top = sq_gettop(v); + sq_pushroottable(v); + sq_pushstring(v, _SC("UI_Render"), -1); + if (SQ_SUCCEEDED(sq_get(v, -2))) + { + sq_pushroottable(v); + sq_pushfloat(v, deltaTime); + sq_call(v, 2, SQFalse, SQTrue); + } + sq_settop(v, top); +} + +void SquirrelEx::Clean() +{ + sq_close(v); + v = nullptr; +} diff --git a/source/squirrel/SquirrelEx.h b/source/squirrel/SquirrelEx.h new file mode 100644 index 0000000..78d47ff --- /dev/null +++ b/source/squirrel/SquirrelEx.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include // Squirrel核心头文件 +#include // Squirrel标准IO库 +#include // 新增:包含 sqstd_seterrorhandlers 等辅助函数 +#include // 新增:包含 sqstd_register_bloblib 函数 +#include // 新增:包含 sqstd_register_systemlib 函数 +#include // 新增:包含 sqstd_register_mathlib 函数 +#include // 新增:包含 sqstd_register_stringlib 函数 +#include +#include + +#include "Tool/Tool_Network.h" + +#ifdef SQUNICODE + +#define scvprintf vfwprintf +#else + +#define scvprintf vfprintf +#endif + +class SquirrelEx +{ +public: + SquirrelEx(const SquirrelEx &) = delete; + SquirrelEx &operator=(const SquirrelEx &) = delete; + SquirrelEx(SquirrelEx &&) = delete; + SquirrelEx &operator=(SquirrelEx &&) = delete; + // 全局访问点 + static SquirrelEx &GetInstance() + { + static SquirrelEx instance; // 局部静态变量,保证只初始化一次 + return instance; + } + + // 打印控制台 + static void printfunc(HSQUIRRELVM v, const SQChar *s, ...); + // 错误打印控制台 + static void errorfunc(HSQUIRRELVM v, const SQChar *s, ...); + // 从字符串编译Sqr脚本 + bool Compilebuffer(std::string Path, std::string Code); + +public: + // 初始化 + void Init(); + // 请求网络脚本 + void RequestNetScript(std::string Ip, std::string Port); + // 运行 + void Run(); + // UiRender入口 + void UiRender(float deltaTime); + // 清理 + void Clean(); + +private: + SquirrelEx(/* args */); + ~SquirrelEx(); + HSQUIRRELVM v = nullptr; +}; diff --git a/source/squirrel/sqr_sdl.hpp b/source/squirrel/sqr_sdl.hpp new file mode 100644 index 0000000..7ff00ef --- /dev/null +++ b/source/squirrel/sqr_sdl.hpp @@ -0,0 +1,146 @@ +#pragma once +#include +#include // Squirrel核心头文件 +#include // Squirrel标准IO库 +#include // 新增:包含 sqstd_seterrorhandlers 等辅助函数 +#include // 新增:包含 sqstd_register_bloblib 函数 +#include // 新增:包含 sqstd_register_systemlib 函数 +#include // 新增:包含 sqstd_register_mathlib 函数 +#include // 新增:包含 sqstd_register_stringlib 函数 +#include "EngineCore/Asset_ImagePack.h" +#include "EngineCore/Game.h" + +#include +#include +#include + +// 辅助函数:检查SDL错误并抛出Squirrel异常 +static void checkSDLError(HSQUIRRELVM v) +{ + const char *err = SDL_GetError(); + if (err && *err) + { + sq_throwerror(v, err); + SDL_ClearError(); + } +} + +// SDL_GetTicks 绑定 +static SQInteger sdl_GetTicks(HSQUIRRELVM v) +{ + sq_pushinteger(v, SDL_GetTicks()); + return 1; +} + +static SQInteger sdl_CreateTexture(HSQUIRRELVM v) +{ + const SQChar *imgPath; + SQInteger Index; + sq_getstring(v, 2, &imgPath); + sq_getinteger(v, 3, &Index); + + Asset_ImagePack::IMG *Info = Asset_ImagePack::GetInstance().GetIMG(imgPath); + Asset_ImagePack::ImgInfo &Buf = Info->lp_lplist[Index]; + SDL_Texture *m_texture = SDL_CreateTexture( + Game::GetInstance().GetRenderer(), + SDL_PIXELFORMAT_ARGB8888, // 匹配RGBA数据格式 + SDL_TEXTUREACCESS_STREAMING, + Buf.Width, Buf.Height); + int pitch = Buf.Width * 4; + SDL_UpdateTexture(m_texture, NULL, Buf.PNGdata, pitch); + + sq_pushuserpointer(v, m_texture); + return 1; +} + +static SQInteger sdl_DrawImg(HSQUIRRELVM v) +{ + SQUserPointer m_texture; + SQInteger PosX, PosY, Width, Height; + + sq_getuserpointer(v, 2, &m_texture); + sq_getinteger(v, 3, &PosX); + sq_getinteger(v, 4, &PosY); + sq_getinteger(v, 5, &Width); + sq_getinteger(v, 6, &Height); + + SDL_Rect Rect = {(int)PosX, (int)PosY, (int)Width, (int)Height}; + SDL_RenderCopy(Game::GetInstance().GetRenderer(), (SDL_Texture *)m_texture, NULL, &Rect); + + return 0; +} + +static SQInteger sdl_DrawImgEx(HSQUIRRELVM v) +{ + const SQChar *imgPath; + SQInteger Index, PosX, PosY, BlendMode; + SQFloat Rotate; + + sq_getstring(v, 2, &imgPath); + sq_getinteger(v, 3, &Index); + sq_getinteger(v, 4, &PosX); + sq_getinteger(v, 5, &PosY); + sq_getinteger(v, 6, &BlendMode); + sq_getfloat(v, 7, &Rotate); + + Asset_ImagePack::IMG *Info = Asset_ImagePack::GetInstance().GetIMG(imgPath); + Asset_ImagePack::ImgInfo &Buf = Info->lp_lplist[Index]; + SDL_Point pivotPoint = { + (int)(0.5 * Buf.Width), + (int)(0.5 * Buf.Height)}; + + SDL_Texture *m_texture = SDL_CreateTexture( + Game::GetInstance().GetRenderer(), + SDL_PIXELFORMAT_ARGB8888, // 匹配RGBA数据格式 + SDL_TEXTUREACCESS_STREAMING, + Buf.Width, Buf.Height); + SDL_SetTextureBlendMode(m_texture, (SDL_BlendMode)BlendMode); + int pitch = Buf.Width * 4; + SDL_UpdateTexture(m_texture, NULL, Buf.PNGdata, pitch); + SDL_Rect Rect = {(int)PosX, (int)PosY, Buf.Width, Buf.Height}; + SDL_RenderCopyEx(Game::GetInstance().GetRenderer(), m_texture, NULL, &Rect, Rotate, &pivotPoint, SDL_FLIP_NONE); + SDL_DestroyTexture(m_texture); + + return 0; +} + +void RegisterSDLFunctions(HSQUIRRELVM v) +{ + // 创建SDL命名空间 + sq_pushstring(v, "SDL", -1); + sq_newtable(v); + + // 注册SDL函数 + + // 获取Ticks函数 + sq_pushstring(v, "GetTicks", -1); + sq_newclosure(v, sdl_GetTicks, 0); + sq_setparamscheck(v, 1, NULL); + sq_rawset(v, -3); + + // 创建纹理 + sq_pushstring(v, "CreateTexture", -1); + sq_newclosure(v, sdl_CreateTexture, 0); + // sq_setparamscheck(v, 3, NULL); + sq_rawset(v, -3); + + // 渲染一个Img + sq_pushstring(v, "DrawImg", -1); + sq_newclosure(v, sdl_DrawImg, 0); + // sq_setparamscheck(v, 5, NULL); + sq_rawset(v, -3); + + // 渲染一个ImgEx + sq_pushstring(v, "DrawImgEx", -1); + sq_newclosure(v, sdl_DrawImgEx, 0); + sq_rawset(v, -3); + + // 注册SDL常量 + + sq_pushstring(v, "RENDERER_ACCELERATED", -1); + sq_pushinteger(v, SDL_RENDERER_ACCELERATED); + sq_rawset(v, -3); + + // 将SDL表添加到全局命名空间 + sq_rawset(v, -3); +} \ No newline at end of file diff --git a/source_game/Asset/AnimationStruct.h b/source_game/Asset/AnimationStruct.h new file mode 100644 index 0000000..89fe63e --- /dev/null +++ b/source_game/Asset/AnimationStruct.h @@ -0,0 +1,137 @@ +#pragma once +#include +#include +#include +#include +#include + +using AniFlag = std::variant< + int, + float, + SDL_Point, + SDL_FPoint, + std::string, + std::vector, + std::vector>; +struct AniFrame +{ + std::string Img_Path; // img路径 + int Img_Index; // img索引 + SDL_Point Img_Pos; // img位置 + std::vector> AttackBox; // 攻击框 + std::vector> DamageBox; // 受击框 + std::unordered_map Flag; // Frame特效数据 + int Delay; // 延迟 +}; +struct AniInfo +{ + std::vector Img_List; // img列表 + std::vector Frame; // ani列表 + std::unordered_map Flag; // ani特效数据 +}; +namespace AniScriptParser +{ + static std::string Get_Ani_Flag(int data) + { + switch (data) + { + case 0: + return "LOOP"; + case 1: + return "SHADOW"; + case 3: + return "COORD"; + case 7: + return "IMAGE_RATE"; + case 8: + return "IMAGE_ROTATE"; + case 9: + return "RGBA"; + case 10: + return "INTERPOLATION"; + case 11: + return "GRAPHIC_EFFECT"; + case 12: + return "DELAY"; + case 13: + return "DAMAGE_TYPE"; + case 14: + return "DAMAGE_BOX"; + case 15: + return "ATTACK_BOX"; + case 16: + return "PLAY_SOUND"; + case 17: + return "PRELOAD"; + case 18: + return "SPECTRUM"; + case 23: + return "SET_FLAG"; + case 24: + return "FLIP_TYPE"; + case 25: + return "LOOP_START"; + case 26: + return "LOOP_END"; + case 27: + return "CLIP"; + case 28: + return "OPERATION"; + default: + return ""; + } + } + + static std::string Get_Ani_Effect_Type(int data) + { + switch (data) + { + case 0: + return "NONE"; + case 1: + return "DODGE"; + case 2: + return "LINEARDODGE"; + case 3: + return "DARK"; + case 4: + return "XOR"; + case 5: + return "MONOCHROME"; + case 6: + return "SPACEDISTORT"; + default: + return ""; + } + }; + + static std::string Get_Ani_Flip_Type(int data) + { + switch (data) + { + case 1: + return "HORIZON"; + case 2: + return "VERTICAL"; + case 3: + return "ALL"; + default: + return ""; + } + }; + + static std::string Get_Ani_Damage_Type(int data) + { + switch (data) + { + case 0: + return "NORMAL"; + case 1: + return "SUPERARMOR"; + case 2: + return "UNBREAKABLE"; + default: + return ""; + } + }; +}; \ No newline at end of file diff --git a/source_game/Asset/AssetManager.cpp b/source_game/Asset/AssetManager.cpp new file mode 100644 index 0000000..9fc748b --- /dev/null +++ b/source_game/Asset/AssetManager.cpp @@ -0,0 +1,264 @@ +#include "AssetManager.h" +#include "EngineCore/Asset_Script.h" +#include "Tool/Blob.hpp" +#include "Tool/Tool_String.hpp" + +AssetManager::AssetManager() +{ +} +AssetManager::~AssetManager() +{ +} + +AniInfo AssetManager::StructAniInfo(std::string path) +{ + AniInfo Info; + std::vector Data = Asset_Script::GetInstance().GetFileContentByte(path); + Blob blob(Data); + + int Frame_Max = blob.getUShort(); + int Img_Count = blob.getUShort(); + + printf("Frame_Max : %d\n", Frame_Max); + printf("Img_Count : %d\n", Img_Count); + + // Img的路径读取 存入数组 + for (int i = 0; i < Img_Count; i++) + { + int Buf = blob.getInt(); + std::string ImgPath = blob.getString(Buf); + printf("ImgPath : %s\n", ImgPath.c_str()); + Info.Img_List.push_back(ImgPath); + } + + // Ani头部标签数量 + int Ani_H_Item_Count = blob.getUShort(); + // 处理标签 + for (int i = 0; i < Ani_H_Item_Count; i++) + { + int Type = blob.getUShort(); + switch (Type) + { + case 0: + case 1: + { + std::string Key = AniScriptParser::Get_Ani_Flag(Type); + int Value = blob.getByte(); + Info.Flag.emplace(Key, Value); + break; + } + case 3: + case 28: + { + std::string Key = AniScriptParser::Get_Ani_Flag(Type); + int Value = blob.getUShort(); + Info.Flag.emplace(Key, Value); + break; + } + case 18: + { + blob.getByte(); + blob.getInt(); + blob.getInt(); + blob.getInt(); + blob.get256(); + blob.get256(); + blob.get256(); + blob.get256(); + blob.getUShort(); + } + default: + break; + } + } + + // 读取每一个Img + for (int i = 0; i < Frame_Max; i++) + { + AniFrame FrameObject; + + // 碰撞框项目数量 + int Ani_Box_Item_Count = blob.getUShort(); + for (int j = 0; j < Ani_Box_Item_Count; j++) + { + int Box_Type = blob.getUShort(); + std::vector D_Box_b; + for (int k = 0; k < 6; k++) + { + D_Box_b.push_back(blob.getInt()); + } + if (Box_Type == 15) + FrameObject.AttackBox.push_back(D_Box_b); + else + FrameObject.DamageBox.push_back(D_Box_b); + } + + // 调用的第几个Img + int Index_Buf = blob.getShort(); + if (Index_Buf != -1) + { + FrameObject.Img_Path = Tool_toLowerCase(Info.Img_List[Index_Buf]); + FrameObject.Img_Index = blob.getUShort(); + } + else + { + FrameObject.Img_Path = ""; + FrameObject.Img_Index = 0; + } + + // 坐标 + FrameObject.Img_Pos = SDL_Point{blob.getInt(), blob.getInt()}; + + // Img中的项目数量 + int Img_Flag_Count = blob.getUShort(); + for (int j = 0; j < Img_Flag_Count; j++) + { + int Img_Flag_Type = blob.getUShort(); + std::string Key; + int Value; + switch (Img_Flag_Type) + { + case 0: + case 1: + case 10: + { + Key = AniScriptParser::Get_Ani_Flag(Img_Flag_Type); + Value = blob.getInt(); + FrameObject.Flag.emplace(Key, Value); + break; + } + case 3: + { + Key = "COORD"; + Value = blob.getUShort(); + FrameObject.Flag.emplace(Key, Value); + break; + } + case 17: + { + Key = "PRELOAD"; + Value = 1; + FrameObject.Flag.emplace(Key, Value); + break; + } + case 7: + { + Key = "IMAGE_RATE"; + SDL_FPoint pos{ + blob.getFloat(), + blob.getFloat()}; + FrameObject.Flag.emplace(Key, pos); + break; + } + case 8: + { + Key = "IMAGE_ROTATE"; + Value = blob.getFloat(); + FrameObject.Flag.emplace(Key, Value); + break; + } + case 9: + { + Key = "RGBA"; + std::vector RGBA = { + blob.get256(), + blob.get256(), + blob.get256(), + blob.get256()}; + FrameObject.Flag.emplace(Key, RGBA); + break; + } + case 11: + { + int Effect_Type = blob.getUShort(); + Key = "GRAPHIC_EFFECT_" + AniScriptParser::Get_Ani_Effect_Type(Effect_Type); + std::vector effect; + switch (Effect_Type) + { + case 5: + { + effect.push_back(blob.get256()); + effect.push_back(blob.get256()); + effect.push_back(blob.get256()); + break; + } + case 6: + { + effect.push_back(blob.get256()); + effect.push_back(blob.get256()); + break; + } + } + FrameObject.Flag.emplace(Key, effect); + break; + } + case 12: + { + Value = blob.getInt(); + FrameObject.Delay = Value; + break; + } + case 13: + { + Key = "DAMAGE_TYPE"; + std::string DTYPE = AniScriptParser::Get_Ani_Damage_Type(blob.getUShort()); + FrameObject.Flag.emplace(Key, DTYPE); + break; + } + case 16: + { + int SoundTempSize = blob.getInt(); + Key = "PLAY_SOUND"; + std::string sound = blob.getString(SoundTempSize); + FrameObject.Flag.emplace(Key, sound); + break; + } + case 23: + { + Key = "SET_FLAG"; + Value = blob.getInt(); + FrameObject.Flag.emplace(Key, Value); + break; + } + case 24: + { + Key = "FLIP_TYPE"; + std::string FTYPEValue = AniScriptParser::Get_Ani_Flip_Type(blob.getUShort()); + FrameObject.Flag.emplace(Key, FTYPEValue); + break; + } + case 25: + { + Key = "LOOP_START"; + FrameObject.Flag.emplace(Key, 1); + break; + } + case 26: + { + Key = "LOOP_END"; + Value = blob.getInt(); + FrameObject.Flag.emplace(Key, Value); + break; + } + case 27: + { + Key = "CLIP"; + std::vector ClipArr{ + blob.getShort(), + blob.getShort(), + blob.getShort(), + blob.getShort(), + }; + FrameObject.Flag.emplace(Key, ClipArr); + break; + } + default: + break; + } + } + + Info.Frame.push_back(FrameObject); + } + + return Info; +} diff --git a/source_game/Asset/AssetManager.h b/source_game/Asset/AssetManager.h new file mode 100644 index 0000000..fa1b685 --- /dev/null +++ b/source_game/Asset/AssetManager.h @@ -0,0 +1,26 @@ +#pragma once +#include "Asset/AnimationStruct.h" //Ani结构 + +class AssetManager +{ + +public: + AssetManager(const AssetManager &) = delete; + AssetManager &operator=(const AssetManager &) = delete; + AssetManager(AssetManager &&) = delete; + AssetManager &operator=(AssetManager &&) = delete; + // 全局访问点 + static AssetManager &GetInstance() + { + static AssetManager instance; // 局部静态变量,保证只初始化一次 + return instance; + } + +private: + AssetManager(/* args */); + ~AssetManager(); + +public: + // 构造Ani结构体 + AniInfo StructAniInfo(std::string path); +}; diff --git a/source_game/Global/Global_Game.cpp b/source_game/Global/Global_Game.cpp new file mode 100644 index 0000000..82de11d --- /dev/null +++ b/source_game/Global/Global_Game.cpp @@ -0,0 +1,21 @@ +#include "Global_Game.h" + +Global_Game::Global_Game() +{ +} +Global_Game::~Global_Game() +{ +} + +void Global_Game::Init() +{ + // 初始化ttf字体资源 + TTF_Font *FontBuf = TTF_OpenFont("Fonts/LXGWWenKai-Regular.ttf", 24); + // TTF_Font *FontBuf = TTF_OpenFont("Fonts/Gothica-Book.ttf", 24); + // TTF_Font *FontBuf = TTF_OpenFont("Fonts/Gasinamu.ttf", 24); + if (!FontBuf) + { + SDL_LogError(0, "字体加载失败: %s", TTF_GetError()); + } + Fonts.push_back(FontBuf); +} \ No newline at end of file diff --git a/source_game/Global/Global_Game.h b/source_game/Global/Global_Game.h new file mode 100644 index 0000000..b9b5722 --- /dev/null +++ b/source_game/Global/Global_Game.h @@ -0,0 +1,31 @@ +#pragma once +#include "EngineCore/Game.h" + +class Global_Game +{ +public: + Global_Game(const Global_Game &) = delete; + Global_Game &operator=(const Global_Game &) = delete; + Global_Game(Global_Game &&) = delete; + Global_Game &operator=(Global_Game &&) = delete; + // 全局访问点 + static Global_Game &GetInstance() + { + static Global_Game instance; // 局部静态变量,保证只初始化一次 + return instance; + } + + void Init(); + +public: + // 当前游戏状态 0未初始化 + int game_state = 0; + +public: + // 字体资源 + std::vector Fonts; + +private: + Global_Game(/* args */); + ~Global_Game(); +}; diff --git a/source_game/Scene/Scene_Loading_UI.cpp b/source_game/Scene/Scene_Loading_UI.cpp new file mode 100644 index 0000000..c1673b5 --- /dev/null +++ b/source_game/Scene/Scene_Loading_UI.cpp @@ -0,0 +1,82 @@ +#include "Scene/Scene_Loading_UI.h" +#include "Scene/Scene_SelectCharacter_UI.hpp" +#include "Scene/Scene_Test.h" +#include "Scene_Loading_UI.h" +#include "EngineFrame/Component/Sprite.h" +#include "EngineFrame/Component/Text.h" +#include "Global/Global_Game.h" +#include "EngineCore/Asset_Script.h" + +Scene_Loading_UI::Scene_Loading_UI(/* args */) +{ +} + +Scene_Loading_UI::~Scene_Loading_UI() +{ +} + +void Scene_Loading_UI::Enter() +{ + // 加载OGG文件 + music = Mix_LoadMUS("Music/characterSelectStage.ogg"); + if (music) + { + Mix_PlayMusic(music, 1); + } + + RefPtr actor = new Actor; + AddChild(actor); + + RefPtr BackGroundSp = new Sprite("sprite/interface2/nowloading/nowloading.img", 1); + actor->AddComponent(BackGroundSp); + RefPtr BackGround2Sp = new Sprite("sprite/interface2/nowloading/nowloading.img", 0); + BackGround2Sp->SetPos(SDL_Point{0, 686}); + actor->AddComponent(BackGround2Sp); + RefPtr LoadCircleSp = new Sprite("sprite/interface2/nowloading/nowloading.img", 4); + LoadCircleSp->SetName("LoadCircle"); + LoadCircleSp->SetPos(SDL_Point{1280 - 60, 686 - 60}); + LoadCircleSp->SetBlendMode(SDL_BLENDMODE_ADD); + LoadCircleSp->SetAnchor(SDL_FPoint{0.5, 0.5}); + actor->AddComponent(LoadCircleSp); + + actor->SetCallbackOnUpdate([LoadCircleSp](float deltaTime) mutable + { + float angle = LoadCircleSp->GetAngle(); + LoadCircleSp->SetAngle(angle + 180.0f * deltaTime); }); + + // 文字测试 + // RefPtr text = new Text("测试文字加载中...", Global_Game::GetInstance().Fonts[0], SDL_Color{255, 255, 255, 255}, SDL_Color{0, 0, 0, 255}, 4); + // actor->AddComponent(text); +} + +void Scene_Loading_UI::HandleEvents(SDL_Event *e) +{ +} + +void Scene_Loading_UI::Update(float deltaTime) +{ + Scene::Update(deltaTime); + if (Asset_Script::GetInstance().InitFlag) + { + // 设定游戏层场景 + RefPtr scene = new Scene_Test; + Game::GetInstance().ChangeScene(scene); + // 设定UI层场景 + RefPtr sceneUI = new Scene_SelectCharacter_UI; + Game::GetInstance().ChangeUIScene(sceneUI); + } +} + +void Scene_Loading_UI::Render(float deltaTime) +{ + Scene::Render(deltaTime); +} + +void Scene_Loading_UI::Exit() +{ + if (music) + { + Mix_FreeMusic(music); + music = nullptr; + } +} diff --git a/source_game/Scene/Scene_Loading_UI.h b/source_game/Scene/Scene_Loading_UI.h new file mode 100644 index 0000000..dddd7ce --- /dev/null +++ b/source_game/Scene/Scene_Loading_UI.h @@ -0,0 +1,22 @@ +#pragma once +#include "EngineFrame/Scene/Scene.h" +#include + +class Scene_Loading_UI : public Scene +{ +private: + /* data */ +public: + Scene_Loading_UI(/* args */); + ~Scene_Loading_UI(); + +public: + void Enter() override; + void HandleEvents(SDL_Event *e) override; + void Update(float deltaTime) override; + void Render(float deltaTime) override; + void Exit() override; + + //加载界面音乐 + Mix_Music *music = nullptr; +}; diff --git a/source_game/Scene/Scene_SelectCharacter_UI.hpp b/source_game/Scene/Scene_SelectCharacter_UI.hpp new file mode 100644 index 0000000..9820ff5 --- /dev/null +++ b/source_game/Scene/Scene_SelectCharacter_UI.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "EngineFrame/Scene/Scene.h" +#include "Asset/AssetManager.h" + +class Scene_SelectCharacter_UI : public Scene +{ +private: + /* data */ +public: + Scene_SelectCharacter_UI(/* args */) { + + }; + ~Scene_SelectCharacter_UI() { + + }; + +public: + void Enter() override + { + AssetManager::GetInstance().StructAniInfo("common/training/main/main.ani"); + SDL_Log("进入了选择角色场景!"); + }; + // void HandleEvents(SDL_Event *e) override; + // void Update(float deltaTime) override; + // void Render(float deltaTime) override; + // void Exit() override; +}; diff --git a/source_game/Scene/Scene_Test.cpp b/source_game/Scene/Scene_Test.cpp new file mode 100644 index 0000000..ac07b85 --- /dev/null +++ b/source_game/Scene/Scene_Test.cpp @@ -0,0 +1,33 @@ +#include "Scene_Test.h" +#include +#include +Scene_Test::Scene_Test() +{ +} + +Scene_Test::~Scene_Test() +{ + SDL_Log("Scene_Test::我的ID是%d --- 我被释放了!", this->MyId); +} + +void Scene_Test::Enter() +{ + SDL_Log("进入测试场景!"); +} + +void Scene_Test::HandleEvents(SDL_Event *e) +{ +} + +void Scene_Test::Update(float deltaTime) +{ +} + +void Scene_Test::Render(float deltaTime) +{ +} + +void Scene_Test::Exit() +{ + SDL_Log("Scene_Test::退出测试场景!当前引用计数%d", this->GetRefCount()); +} diff --git a/source_game/Scene/Scene_Test.h b/source_game/Scene/Scene_Test.h new file mode 100644 index 0000000..7cba80f --- /dev/null +++ b/source_game/Scene/Scene_Test.h @@ -0,0 +1,21 @@ +#pragma once +#include "EngineFrame/Scene/Scene.h" + +class Scene_Test : public Scene +{ +private: + /* data */ +public: + Scene_Test(/* args */); + ~Scene_Test(); + +public: + void Enter() override; + void HandleEvents(SDL_Event *e) override; + void Update(float deltaTime) override; + void Render(float deltaTime) override; + void Exit() override; + +public: + int MyId = 0; +};