维护提醒

BWIKI 全站将于 9 月 3 日(全天)进行维护,期间无法编辑任何页面或发布新的评论。

全站通知:

模组:制作指南/游戏基本架构

来自星露谷物语维基
跳到导航 跳到搜索

模组:目录

本页面将简单阐述部分游戏内的概念以帮助您制作模组。配合常见问题食用体验更佳。

一些概念

时间格式

游戏内时间采用一种特殊的24小时制,暂且称为“26小时制”,按10分钟为步长计时。这也是C# modContent Patcher包所使用的格式。

样例时间

时间值 显示文本
600 6:00 am
1250 12:50 am
1300 1:00 pm
2600 2:00 am (睡前)

内部时间在睡觉之前都会一直增加(例如,次日6时会在这种情况下变为3000)。

地块

星露谷是由一个个地块(tile,或称图块)组成的。我们又为它们定义了自己的坐标,如 (0, 0)表示左上角。横坐标越往右值越大,纵坐标越往下值越大。

Modding - creating an XNB mod - tile coordinates.png

坐标

定位方式 参照点 解释
地块坐标 (tile position) 地图左上角 地块为单位。通常用于物品放置,如:location.Objects
绝对坐标 (absolute position) 地图左上角 像素为单位。用于更精确的测量,如:NPC的位置。
屏幕坐标 (screen position) 可见(visible) 的屏幕的左上角 像素为单位。通常用于绘制。

换算:

A → B 公式
绝对坐标 屏幕坐标 x - Game1.viewport.X, y - Game1.viewport.Y
绝对坐标 地块坐标 x / Game1.tileSize, y / Game1.tileSize
屏幕坐标 绝对坐标 x + Game1.viewport.X, y + Game1.viewport.Y
屏幕坐标 地块坐标 (x + Game1.viewport.X) / Game1.tileSize, (y + Game1.viewport.Y) / Game1.tileSize
地块坐标 绝对坐标 x * Game1.tileSize, y * Game1.tileSize
地块坐标 屏幕坐标 (x * Game1.tileSize) - Game1.viewport.X, (y * Game1.tileSize) - Game1.viewport.Y

缩放等级

玩家可以设置缩放等级(zoom level),从75%到200%。这会影响屏幕上显示的像素大小。

缩放至最小 (75%) 缩放至最大 (200%)
Zoom level 75.png Zoom level 200.png

对于SMAPI模组的影响

缩放等级储存在Game1.options.zoomLevel中。游戏会自动调整坐标绘制大小,很少有情况需要你亲自调整。万一有,你可以position * (1f / Game1.options.zoomLevel)手动调。

UI缩放

在1.5版本中,游戏加入了缩放UI(UI scaling)的功能。这是一个独立于上一节的缩放的新功能,特别用于缩放用户界面,最小75%,最大150%。

UI最小显示 (75%) UI最大显示 (150%)
UI scale 75.png UI scale 150.png

对于SMAPI模组的影响

游戏有两种缩放模式,UI 和 非UI。当前模式储存在Game1.uiMode中。不要将两种模式混淆,这会带来复杂的计算。

下表是游戏使用两种缩放模式的不同情景:

情景 缩放模式
菜单 (clickable menus) UI模式 (通常)
HUD消息 UI模式
RenderingActiveMenu事件
RenderedActiveMenu事件
UI模式
Rendering事件
Rendered事件
取决于具体上下文
draw方法 非UI模式
地块(而非像素)坐标 不受UI缩放影响

当游戏不在UI模式中,而你又想绘制UI时,请手动设置:

Game1.game1.InUIMode(() =>
{
   // ...
});

在 UI 模式中,您通常应当将 Game1.viewport 换为 Game1.uiViewport。如果您此外还需另行为 UI 缩放调整坐标,则不要这样做。因为双重转换会得到错误的结果。您可以使用 Utility.ModifyCoordinatesForUIScaleModifyCoordinatesFromUIScale 来转换 UI坐标 和 非UI坐标。

您可以这样测试模组能否能正确处理这点:把缩放等级设为最大,把 UI 缩放设为最小(即,使两个数值显著对立),或反过来;请注意检查需要处理像素位置的逻辑,例如菜单或点击。

C#模组的多人游戏概念

Net字段

所谓Net类型(Net type)是指游戏用于在玩家间同步数据的几个类,而Net字段(Net field)是指Net类型的任一字段或属性。其类型名称带有Net前缀。Net类型既可以表示像NetBool这样的简单数值,也可以表示像NetFieldDictionary这样的复杂数值。每隔一段时间,游戏便会从Game1.netWorldState尽可能收集所有Net字段,以与其他玩家同步。这表明,很多因模组做出的改变会自动被游戏同步。

尽管net类型自带隐式转换,如:bool x = new NetBool(true);,但并不稳定,很可能会跟你所想达成的目标有出入,如:item?.category == nullitem?.category != null 有可能会同时为true。 因此,请避免用net类型的隐式转换


Blue Chicken.png
即将到来

下面的内容或特性计划在即将到来的 1.6.9 版本 SMAPI 实装,描述的内容可能会和正式发布的版本有差异。
在游戏本体 1.6.9 版本后将不再提供 Net 类型的隐式转换。

具体如何拿到对应的值见下表:

net类型 说明
NetBool
NetColor
NetFloat
NetInt
NetPoint
NetString
这些属于简单的net类型。通过field.Value拿到对应值。
NetCollection<T>
NetList<T>
NetObjectList<T>
T的集合。直接遍历 foreach (T value in field)
NetLongDictionary<TValue, TNetValue>
NetPointDictionary<TValue, TNetValue>
NetVector2Dictionary<TValue, TNetValue>
Long, Point, 或Vector2 类型的键映射至 TValue (原类型) 和 TNetValue (用于同步的net类型)。遍历键值对,如:foreach (KeyValuePair<Long, TValue> pair in field.Pairs)

农场帮手影子世界

多人游戏中,客机(农场帮手)不会看见大多数游戏地点。实际上,他们的游戏会在他们加入服务器前为世界创建一个单人模式的拷贝;加入服务器后,则仅从房主处取回农场区域和农场帮手所在地点(称之活跃区域)。未同步的地点可能与其他玩家不同。

这对于 C# 模组有一些重要的启示:

  • Game1.locations 同时列出了活跃地点和影子地点。当模组处理影子地点时,起获得的数据可能并非服务器上的真实数据,相应地,对这些影子地点所作的修改也不会同步到服务器上。
  • 在影子世界中可能存在 NPC、马等影子对象,与真实的对象发生重复。只有在活跃地点,这些对象才是真实的
  • 游戏方法(例如 Game1.getCharacterByName)可能无法正确识别“真实”和“影子”对象。
  • 当一个农场帮手被传送往另一个地点时,在传送完毕后游戏会从房主处取回真实的地点数据。因此,农场帮手在传送的一瞬间,其 currentLocation 可能为空。

您可以调用地点的 IsActiveLocation 方法来检测其是否为活跃地点:

foreach (GameLocation location in Game1.locations)
{
    if (!location.IsActiveLocation())
        continue; // shadow location

    ...
}

主要的类

Game1

游戏主要逻辑。 下面是一些重要的成员:

字段、属性 类型 描述
Game1.player Farmer 当前玩家。
Game1.currentLocation GameLocation 当前玩家所在的地点。 对于联机中客机玩家,在地点变化时可能为null
Game1.locations IList<GameLocation> 所有地点。对于联机中客机玩家,请使用SMAPI的GetActiveLocations方法代替。
Game1.timeOfDay
Game1.dayOfMonth
Game1.currentSeason
Game1.year
int
int
string
int
当前时间、日期、季节、年份。另见SMAPI的日期工具类
Game1.itemsToShip IList<Item> 不要用这个,这是存档逻辑的一部分。请用Game1.getFarm().getShippingBin(Farmer)
Game1.activeClickableMenu IClickableMenu 当前显示的菜单。

GameLocation et al

  • GameLocation 表示地点,内有地图、物品、角色、动植物等等数据。下表是一些重要的成员:
    字段、属性 类型 描述
    Name string 唯一名称。(这对于建筑室内并不唯一,见uniqueName。)
    IsFarm bool 是否为农场(农场内可种植作物)。
    IsGreenhouse bool 是否为温室(温室内可种植作物,且全年可生长)。
    IsOutdoors bool 是否为室外。
    characters NPC 组成的 NetCollection 该地点中的村民、宠物、马、怪物。
    critters 私有方法 Critter 组成的 List 该地点中暂时的鸟、松鼠等等,用于衬托景色。
    debris Debris 组成的 NetCollection 掉落物(漂浮的物品)。
    farmers FarmerCollection 该地点中的玩家。
    Objects OverlaidDictionary 该地点中所有物品。(OverlaidDictionary实际上是一个NetVector2Dictionary,并额外增加了show certain quest items over pre-existing objects 的逻辑。)
    terrainFeatures TerrainFeature 组成的 NetVector2Dictionary 该地点中的树、果树、草、泥土地(其中包括农作物)、地板等等。
    waterTiles bool[,] 一个多维数组表示地图图块是否为代表河/湖。如:if (location.waterTiles[10, 20])将检查坐标为(10, 20)的图块。
  • BuildableGameLocationGameLocation的子类。表示支持建造建筑的地点。原版游戏中,只有FarmBuildableGameLocation。下面是一些重要的成员:
    字段 类型 描述
    buildings Building 组成的 NetCollection 该地点中的建筑。
  • FarmGameLocationBuildableGameLocation 的子类。玩家可在农场中养动物、种庄稼。原版游戏中,Farm类型的实例地点只有一个(Game1.getFarm();)。下面是一些重要的成员:
    字段、属性 类型 描述
    animals FarmAnimal 组成的 NetLongDictionary The farm animals currently in the location.
    resourceClumps ResourceClump 组成的 NetCollection 该地点中的巨大作物、木桩、大石块、陨石。
    piecesOfHay NetInt 筒仓中储存的稻草数量。
    shippingBin Item 组成的 NetCollection 出货箱里的物品。
  • 针对具体的地点,还存在很多子类(例如 AdventureGuild),它们提供了某些情况下很实用的字段。