Skip to content

Commit

Permalink
测试地图相关 (#10)
Browse files Browse the repository at this point in the history
* stuff

* 修复错误的代码

* Update .gitignore

* 添加测试地图 Loenn教程目录

* Update to_do_list.md

* 移除dll

* 重写通过模板创建项目

* 完善跨mod交互 迁移测试地图至新的模板

* Update docs/advanced/cross_mod_interactions.md

Co-authored-by: Saplonily Mason <[email protected]>

* request stuff

* Update basic_env.md

---------

Co-authored-by: Saplonily Mason <[email protected]>
  • Loading branch information
ClearZer0 and Saplonily authored Dec 22, 2024
1 parent cc90ee3 commit 69ef472
Show file tree
Hide file tree
Showing 41 changed files with 663 additions and 76 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
site/**
.VSCodeCounter/**
.vs/**
.idea/
.idea/
**/Source/bin/**
**/Source/obj/**
**/Source/.vs/**
**/Source/CelesteModTutorial.sln
58 changes: 53 additions & 5 deletions docs/advanced/cross_mod_interactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ public static bool GravityHelperLoaded;
public override void Load()
{
// 获取 GravityHelperModule 的元数据
EverestModuleMetadata gravityHelper = new()
EverestModuleMetadata gravityHelperMetadata = new()
{
Name = "GravityHelper",
Version = new Version(1, 2, 20)
};
// 判断 GravityHelper 是否成功加载
GravityHelperLoaded = Everest.Loader.DependencyLoaded(gravityHelper);
GravityHelperLoaded = Everest.Loader.DependencyLoaded(gravityHelperMetadata);
}
```

Expand Down Expand Up @@ -148,13 +148,61 @@ if (MyCelesteModAPI.MultiplyByTwo(myNumber) > 400)
通过这种方式, 我们可以在自己的 Mod 中访问并调用其他 Mod 提供的功能, 而不需要直接依赖该 Mod 的程序集.


<!--

## 直接程序集引用

有时候我们需要直接使用目标Mod中的类型和方法, 但目标 Mod 并没有实现 `ModInterop` API.
这种情况下, 我们可以直接引用其他 Mod 的程序集.

下面我们介绍两种方法:

### Cache

Everest 会将所有 Code Mod 的程序集使用 MonoMod 进行 patch 处理后放置到 `Celeste/Mods/Cache/<mod名>.<程序集名>.dll` 中.
我们可以通过配置模板的 `.csporj` 文件以直接引用它们:

```xml title="MyCelesteMod.csproj" hl_lines="19 20 21 22"
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="CelesteMod.props" />
<PropertyGroup>
<RootNamespace>Celeste.Mod.MyCelesteMod</RootNamespace>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<None Include="CelesteMod.props">
<Visible>false</Visible>
</None>
<None Include="CelesteMod.targets">
<Visible>false</Visible>
</None>
</ItemGroup>
<ItemGroup>
<CelesteModReference Include="GravityHelper" />
<CelesteModReference Include="ExtendedVariantMode" />
<CelesteModReference Include="FrostHelper" AssemblyName="FrostTempleHelper" />
</ItemGroup>
<Import Project="CelesteMod.targets" />
</Project>
```

!!! info
在引用之前我们需要确认目标 Mod 在 `Cache` 中的是否存在, 以上面引用的 Mod 为例. `Cache` 中应该存在:

- GravityHelper.GravityHelper.dll
- ExtendedVariantMode.ExtendedVariantMode.dll

我们填写目标 Mod 在 `Cache` 中名称的前半段就行.

### lib-stripped
-->

`lib-stripped` 是指剥离了所有方法实现的程序集, 仅保留类型和方法签名.
我们可以通过 [`NStrip`](https://github.com/bbepis/NStrip) 或 [`BepInEx.AssemblyPublicizer `](https://github.com/BepInEx/BepInEx.AssemblyPublicizer) 的 `strip-only` 模式等工具对目标程序集进行剥离.
完成后我们可以直接引用被剥离的程序集.



Expand Down Expand Up @@ -192,7 +240,7 @@ public override void Load()
PlayerGravityComponentProperty = gravityHelperModuleType?.GetProperty("PlayerComponent", BindingFlags.NonPublic | BindingFlags.Static);
// 反射获取 GravityHelper.Components.SetPlayerGravity 方法
SetPlayerGravityMethod = playerComponent?.GetValue(null)?.GetType().GetMethod("SetGravity", BindingFlags.Public | BindingFlags.Instance);
SetPlayerGravityMethod = PlayerGravityComponentProperty?.GetValue(null)?.GetType().GetMethod("SetGravity", BindingFlags.Public | BindingFlags.Instance);
// 反射获取 GravityHelper.GravityHelperModule.ShouldInvertPlayer 属性
IsPlayerInvertedProperty = gravityHelperModuleType?.GetProperty("ShouldInvertPlayer", BindingFlags.Public | BindingFlags.Static);
Expand Down
87 changes: 87 additions & 0 deletions docs/arc/project_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
!!! info
在蔚蓝国外社区流行着另一个 mod 项目模板, 不过我个人不太喜欢它, 不过你需要的话[这是 Github 主页](https://github.com/EverestAPI/CelesteModTemplate)
所以这里主要使用我个人制作也是个人最常用的一个.

\_(:з」∠)\_
根据一些反馈我们发现旧的手动配置环境的方式非常的复杂难操作(
所以呢这里就推荐一种新的配置环境的方式 - **使用模板**
考虑到 nuget 安装模板也需要一定的命令行基础...
所以这里考虑[提供直接的下载链接](https://hongshitieli.lanzouj.com/iJfRz1l0iffg),
或者你也可以选择使用 `dotnet cli` 从 nuget 上的模板安装:

!!! note
你可能还需要安装 `.NET 8 SDK` 来使用该模板, 你可以[在这里](https://get.dot.net)找到它

??? info "使用 dotnet cli 从模板新建项目"
首先在一个你喜欢的位置放置你的项目文件夹, 名字即为你的项目名, 例如 `MyCelesteMod`:
```bat
mkdir MyCelesteMod
cd MyCelesteMod
```
然后在此位置安装 nuget 上我的 mod 模板(如果你没有安装的话):
```bat
dotnet new install Saladim.CelesteModTemplate
```
然后你就能使用这条指令直接创建项目了:
```bat
dotnet new sapcelestemode
```
名字即为上层文件夹名, 或者你可以使用 `-n` 参数重写项目名字:
```bat
dotnet new sapcelestemod -n MySuperCelesteMod
```
模板目前默认不会创建针对 Everest Core 的 Code Mod, 如果你需要的话你可以传入 `--core-only true` 参数:
```bat
dotnet new sapcelestemod --core-only true
```

完成后使用你喜欢的编辑器打开项目(对于 vs 直接打开 .csproj 文件), 那么按理来说你会看到这几个文件:

- CelesteMod.props
- CelesteMod.targets
- Common.props
- MyCelesteModModule.cs
- MyCelesteMod.csproj

以及你的项目, 它的名字是 `MyCelesteMod`, 不同于旧的方法, 在这里你的配置过程很简单:

- 首先打开 `Common.props`, **将里面的 `CelesteRootPath` 内的内容改成你的蔚蓝安装位置**

```xml hl_lines="3"
<Project>
<PropertyGroup>
<CelesteRootPath>C:\Program Files (x86)\Steam\steamapps\common\Celeste</CelesteRootPath>
<CommonCelesteUsings>true</CommonCelesteUsings>
<CommonCelesteReferences>true</CommonCelesteReferences>
<ModAssetsFolderName>ModFolder</ModAssetsFolderName>
</PropertyGroup>
</Project>
```

现在你可以按下 `Ctrl+B` 或者手动点击 `生成->生成解决方案`,
如果你在你的 vs 输出里面看到了类似这两句:

```
1>MyCelesteMod -> D:\User\temp\cm\bin\x86\Debug\net452\MyCelesteMod.dll
1>MyCelesteMod -> C:/Program Files (x86)/Steam/steamapps/common/Celeste/Mods/MyCelesteMod_copy/MyCelesteMod.dll
```

并且你在你的蔚蓝 Mod 目录下找到了这个被创建的目录,
那么你的环境就算是配完了, 如果你很感兴趣这之中发生了什么, 要引用哪些程序集, 这个模板背后干了什么, 你可以去看那复杂的旧的配置方法.
!!! note
这个模板使用 `msbuild` 帮助了你很多事!
比如当你编译完项目之后它会复制编译结果到项目目录的 `ModFolder` 目录下,
然后将整个 `ModFolder` 复制到蔚蓝的 `Mods\{你的mod名}_copy` 文件夹下!
所以当我们需要更改一些比如说 loenn 的配置文件, `everest.yaml` 的内容, 你的测试地图等时,
你只需要简单地重新编译一遍项目, 然后等待模板来帮你做剩下的活!


## 更改细节

通过模板的话依然有些东西需要自行更改, 比如这个 Mod 的名字.
更改 Mod 的名字很简单, 你只需要简单地在 vs 里重命名项目的名字
比如我想叫做 `MyAwesomeMod`, 那么你可以通过这样:
![awesome mod!](rename_proj.png)

顺便别忘了把类似 `MyCelesteModModule.cs` 的文件名也改成类似 `MyAwesomeModModule.cs`,
以及改名后清理一下 ModFolder 下面可能有的一些以过去名字命名的 .dll 和 .pdb 文件!
File renamed without changes
22 changes: 12 additions & 10 deletions docs/coding_challenges/simple_texturing.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ ok 那么现在我们该在代码这边来点贴图了, 这里我们会用到之
- Graphics
- Atlases
- Gameplay
- `MyCelesteMod`
- pass_by_refill.png
- objects
- PassByRefill
- pass_by_refill.png

`pass_by_refill.png` 即我们的贴图, 如果你同时也是一位 mapper 的话你一定很熟悉这个文件夹套套乐!
在代码这边, 我们使用 `GFX.Game["MyCelesteMod/pass_by_refill"]` 来获取这个贴图, 它是一个 `MTexture` 类型的实例, 在获取到这个贴图后,
在代码这边, 我们使用 `GFX.Game["objects/PassByRefill/pass_by_refill"]` 来获取这个贴图, 它是一个 `MTexture` 类型的实例, 在获取到这个贴图后,
我们 `new` 一个 `Monocle.Image`, 然后在构造函数中传入它, 然后使用 `this.Add` 函数挂载到我们的这个实体上, 总的代码应该是这样的:
```cs title="PassByRefill.cs"
public PassByRefill(Vector2 position, int dashes)
Expand All @@ -73,16 +74,17 @@ public PassByRefill(Vector2 position, int dashes)
Hitbox hitbox = new(64, 64);
Collider = hitbox;

MTexture tex = GFX.Game["MyCelesteMod/pass_by_refill"];
MTexture tex = GFX.Game["objects/PassByRefill/pass_by_refill"];
Image image = new(tex);
this.Add(image);
}
```

!!! info
`GFX` 是蔚蓝中的一个管理贴图的类, 我们用它获取到一个贴图组 `Game`, 然后向它检索一个名为 `MyCelesteMod/pass_by_refill` 的贴图, 你可能会疑惑为什么这里的路径只需要后半部分,
这是因为 `GFX.Game` 只会检索 `Atlases/Graphics/Gameplay` 中的内容.
同样的, `GFX.Portraits` 只会检索 `Atlases/Graphics/Portraits` 中的内容.
`GFX` 是蔚蓝中的一个管理贴图的类, 我们用它获取到一个贴图组 `Game`, 然后向它检索一个名为 `objects/PassByRefill/pass_by_refill` 的贴图, 你可能会疑惑为什么这里的路径只需要后半部分,

这是因为 `GFX.Game` 只会检索 `Graphics/Atlases/Gameplay` 中的内容.
同样的, `GFX.Portraits` 只会检索 `Graphics/Atlases/Portraits` 中的内容.

顺便记得删掉我们重写的 `Render` 函数, 我们不再需要它了. 总的类应该是这样的:
```cs title="PassByRefill.cs"
Expand All @@ -99,7 +101,7 @@ public class PassByRefill : Entity
Hitbox hitbox = new(64, 64);
Collider = hitbox;

MTexture tex = GFX.Game["MyCelesteMod/pass_by_refill"];
MTexture tex = GFX.Game["objects/PassByRefill/pass_by_refill"];
Image image = new(tex);
this.Add(image);
}
Expand Down Expand Up @@ -171,7 +173,7 @@ public class PassByRefill : Entity

然后设置 entity 的 `texture` 属性, 这会让 Loenn 为其设置贴图:
```lua
entity.texture = "MyCelesteMod/pass_by_refill"
entity.texture = "objects/PassByRefill/pass_by_refill"
```
顺便设置贴图原点为左上角, 否则 Loenn 中的显示可能会与实际游戏中的不同:
```lua
Expand All @@ -197,7 +199,7 @@ entity.fieldInformation =
}
}

entity.texture = "MyCelesteMod/pass_by_refill"
entity.texture = "objects/PassByRefill/pass_by_refill"
entity.justification = { 0.0, 0.0 }

return entity
Expand Down
8 changes: 8 additions & 0 deletions docs/coding_challenges/test_map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 测试地图

实战部分附带一张测试地图供读者下载下来<del>乱弄</del>研究与测试,
你可以在[这里](../resources/CelesteModTutorial.zip)进行下载.
代码你可以在 `Source` 目录下找到.

!!! info
下载并解压后别忘了生成 Mod 的 `dll` 文件!
Loading

0 comments on commit 69ef472

Please sign in to comment.