星露谷物语mod制作


为什么要写星露谷物语的mod

由于某位沉迷于星露谷物语,而且不想使用效率类mod来提升游戏速度,对于我这种经营类苦手来说实在是太过于煎熬,于是决定自己写一点不是很影响游戏体验并且符合自己需求的mod来提升一下游戏体验。


星露谷物语mod的开发

在决定要写一点东西以后,就是上网寻找资料了,无奈简中网络没有搜索到什么合适的教程,找到比较好的教程还是19年在百度贴吧上的教程,而且图床都已经全部失效,没办法只能去全球最大的同性交友网站看看了。

这里先说一下,星露谷物语的社区开发其实非常完善,有一个很完善的社区支持。

星露谷wiki可以说是这么多年来看到最用心,最完善的游戏社区图鉴/教程集合。不仅拥有风格化界面,内容也事无巨细,相当丰富。

作者埃里克和社区也有相当紧密的合作,smapi是一个社区开发的星露谷mod管理器,smapi不仅可以很方便的管理星露谷mod,而且与游戏官方合作,保持版本更新并提供相当丰富的api接口给开发者,mod开发者可以在这个链接下载开发者版本的smapi获得更多的控制台输出。


怎么完成第一个mod

本教程旨在说明使用C#开发的功能类mod,使用json和自制资源可以方便的完成内容物mod,参见这个链接

这里先贴上一些有用的网站:

星露谷wiki的smapi mod开发新手教程,从头开始讲述如何使用C#开发星露谷的mod。

这里简短的概括一下流程:

  1. 安装星露谷物语(废话),steam掏钱即可(喜,最简单的一步)
  2. 安装smapi,在上面的链接中下载开发者版本的smapi,解压后点击.bat文件并按照提示即可。
  3. 安装开发者工具,这里我使用的是windows,用微软的visual studio 2022,下载会先安装installer,在installer中安装visual studio 2022 community即可,勾选c#开发组建,并在单个组件中勾选.NET 6 SDK
  4. 创建一个类库项目,项目名称是你的mod名称,并选择.NET 6作为基准即可。
  5. 创建项目
  6. 项目-管理Nuget程序包中安装Pathoschild.Stardew.ModBuildConfig包
    在包管理中搜索下载安装即可
  7. 重启vs 2022

以上已经做好了编写一个mod的准备工作,接下来我们看看应该怎么完成第一个mod。

作为一个最最简单的mod,我们需要的只有两个文件:

  1. ModEntry.cs
  2. manifest.json

首先删除默认的class1.cs文件,新建上面两个文件

需要新建的文件

 

 

这是一个简单的例子来说明项目结构

我们这里以一个简单的传送的mod作为示例来说一说里面的类结构

我们需要实现的功能是:在按下手柄左摇杆或着键盘的f1键,即可传送到另一个玩家身边。

 

在smapi启动时,会寻找ModEntry.cs的里面的ModEntry类的Entry方法作为入口,先来看入口定义

附上api文档

Modding:Modder Guide/APIs - Stardew Valley Wiki


using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using System.Collections.Generic;
using System.Linq;

namespace Teleport
{
    public class ModEntry : Mod
    {
        private List _farmers;

        public override void Entry(IModHelper helper)//入口函数
        {
            //helper.Events.GameLoop.GameLaunched += this.OnGameLaunched;
            helper.Events.GameLoop.SaveLoaded += this.OnSaveLoaded;
            //helper.Events.GameLoop.ReturnedToTitle += this.OnReturnedToTitle;
        }

        private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
        {
            Helper.Events.Input.ButtonPressed += this.OnButtonPressed;
            //Helper.Events.Input.ButtonReleased += this.OnButtonReleased;
        }

        private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
        {

        }
    }
}

这里我们先关注Entry方法,这个就是smapi调用的入口方法,这个方法的主要人物是将事件处理函数加到不同的事件池里面。

在这个例子里面添加了saveLoaded事件处理函数,这是一个在加载存档后会调用的方法池,你需要将加载存档时需要读取的数据/加载的内容放在这里,如农民数量等,需要在这里读取。

类似的,如果在OnGameLaunched事件池里添加方法,则可以在加载游戏的时候添加,比如增加一些设置功能等等。

 

private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
{
    this._farmers = Game1.getAllFarmers().Where(f => !string.IsNullOrEmpty(f.Name)).ToList();
    Monitor.Log("Game launched");
    Helper.Events.Input.ButtonPressed += this.OnButtonPressed;
    //Helper.Events.Input.ButtonReleased += this.OnButtonReleased;
}

相同的,我们再来看一下这个OnSaveLoaded函数里面干了些什么,我们在前面声明了一个变量数组_farmers作为一个类的私有变量,在这里我们调用smapi的项目接口Game1.getAllFarmers()来得到所有的农民类,并在monitor中打出log(这个log会打印在smapi的控制台里面),然后再加入一个成员方法OnButtonReleased到ButtonPressed事件池里面。

在接着定义ButtonPressed

private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
{
    if (e.Button == SButton.F1 || e.Button == SButton.LeftStick)
    {
        this.Monitor.Log($"Farmers: {this._farmers.Count}");
        StardewValley.Farmer targetFramer;
        if (this._farmers.Count > 1)
        {
            targetFramer = this._farmers[0] == Game1.player ? this._farmers[1] : this._farmers[0];
        }
        else
        {
            this.Monitor.Log("No other farmers found");
            return;
        }
        this.WarpTo(targetFramer);
    }
}

这个成员函数会在按键被按下的时候触发,并由e.Button传入,用if可以判断如果是F1和LeftStick就调用Warp函数,再用Warp函数处理即可。