废话
我先介绍一下我们在这篇中实现什么样的功能,估计从标题已经猜到了,是原版的太阳能发电机功能。
下面介绍一下原版的代码关于这个建筑的工作原理。
1.首先所有物品的父类是Entity,它定义了物品通用的属性,比如名称,和一些重要方法,例如初始化,生成,回收,每帧回调
2.接着Thing继承了Entity,并实现了选择,序列化,接收信号回调等方法,游戏中所有的实体都是一个Thing的实例
3.ThingWithComps(Thing with components带有组件的物体)继承了Thing,可以继承一些comp组件已完成各式各样灵活复杂的功能。
4.Building继承于ThingWithComps类,高度分化为建筑相关的物体,而我们的太阳能发电机就是一个Building而已。
5.而太阳能发电的功能被编写为一个comp组件,太阳能发电机作为Building实例,集成CompPowerPlantSolar(太阳能工厂电力组件)从而实现太阳能发电机这个建筑。
6.而组件CompPowerPlantSolar又由ThingComp(基础定义)–CompPower(实现了电力相关)–CompPowerTrader(实现电力交换)–CompPowerPlant(发电机)一路继承而来,最终由CompPowerPlant衍生为各式各样的发电机,太阳能发电机属于其中一种。
这种设计方便灵活,假如我们要编写自己的发电机的话,标准做法是继承于CompPowerPlant类,实现我们发电机特别的功能,然后随便实例化一个Building集成我们的电力组件即可。
但是我们今天的重点不在组件上,而是在Building这个Thing本身,我会在Building这个分支演化其他类型的建筑来实现我们的太阳能发电机功能,尽管这不是一个明智的方法。本篇仅为演示功能和学习Thing部分,有关Comp我会择日详谈。
核心内容
1.解析原版太阳能发电机实现
1.1 xml创建物品数据
找到原版太阳能发电机的数据,复制粘贴,改改名字和描述,删掉CompPowerPlantSolar组件,添加CompPowerTrader组件。
为什么这样做?
因为我们要在自己的Thing类中实现CompPowerPlant和CompPowerPlantSolar部分代码的功能。
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<ThingDef ParentName="BuildingBase">
<defName>MySolarGenerator</defName>
<label>自定义太阳能发电机</label>
<description>从阳光中提供电力,夜晚不工作</description>
<thingClass>SR.Building_MySolarGenerator</thingClass>
<graphicData>
<texPath>Things/Building/Power/SolarCollector</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>(4,4)</drawSize>
<shadowData>
<volume>(3.5,0.75,3.4)</volume>
<offset>(0,0,0)</offset>
</shadowData>
<damageData>
<rect>(0,0.6,4,2.8)</rect>
</damageData>
</graphicData>
<castEdgeShadows>true</castEdgeShadows>
<rotatable>false</rotatable>
<altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability>
<pathCost>70</pathCost>
<fillPercent>0.5</fillPercent>
<canOverlapZones>false</canOverlapZones>
<statBases>
<MaxHitPoints>300</MaxHitPoints>
<WorkToBuild>2500</WorkToBuild>
<Flammability>0.7</Flammability>
</statBases>
<tickerType>Normal</tickerType>
<size>(4,4)</size>
<costList>
<Steel>100</Steel>
<ComponentIndustrial>3</ComponentIndustrial>
</costList>
<comps>
<li Class="CompProperties_Power">
<compClass>CompPowerTrader</compClass>
<basePowerConsumption>1</basePowerConsumption>
<transmitsPower>true</transmitsPower>
</li>
<li Class="CompProperties_Breakdownable"/>
</comps>
<terrainAffordanceNeeded>Medium</terrainAffordanceNeeded>
<designationCategory>Power</designationCategory>
<designationHotKey>Misc5</designationHotKey>
<constructEffect>ConstructMetal</constructEffect>
<researchPrerequisites>
<li>Electricity</li>
<li>SolarPanels</li>
</researchPrerequisites>
<constructionSkillPrerequisite>6</constructionSkillPrerequisite>
</ThingDef>
</Defs>
为物品编好数据后便可以进入游戏使用开发者模式测试我们的新建筑。你会发现它只有1的耗电量,并且和正常的太阳能发电机相比少了黄色的电力条。
不要慌,这是正常的,我们还什么都没写,basePowerConsumption默认耗电量是我们定义的,至于黄条,游戏里是用代码绘制的,缺少贴图也正常。
1.2 实现发电功能
using RimWorld;
using Verse;
namespace SR
{
public class Building_MySolarGenerator : Building
{
private const float fullSunPower = 1700f;//日间最大发电量
private const float nightPower = 0f;//夜间发电量
//遮挡因数
private float roofedFactor {
get {
int cells = 0;//建筑面积中占用的坐标总数
int roofedCells = 0;//建筑被屋顶挡住的坐标数量
foreach (var c in GenAdj.OccupiedRect(this.Position,this.Rotation,this.def.size))
{
++cells;
//被屋顶挡住了 影响太阳能效率
if (Map.roofGrid.Roofed(c))
{
++roofedCells;
}
}
return (float)(cells-roofedCells) / (float)cells;
}
}
private float realPower => (fullSunPower-nightPower)*Map.skyManager.CurSkyGlow * roofedFactor;//实际发电量=(最大发电-最小发电)*天气因数*遮挡因数
private CompPowerTrader compPowerTrader;//电力交换组件
/// <summary>
/// 生成时回调
/// </summary>
/// <param name="map"></param>
/// <param name="respawningAfterLoad"></param>
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
base.SpawnSetup(map, respawningAfterLoad);
compPowerTrader = GetComp<CompPowerTrader>() ?? new CompPowerTrader();//寻找电力交换组件
compPowerTrader.PowerOutput = 0;
}
/// <summary>
/// 每帧做什么
/// </summary>
public override void Tick()
{
base.Tick();
compPowerTrader.PowerOutput = realPower;//每一帧提供实际的电力 如果你想做的是电池的话,PowerOutput可以传负值进去
}
/// <summary>
/// 销毁时的回调
/// </summary>
/// <param name="mode"></param>
public override void Destroy(DestroyMode mode = DestroyMode.Vanish)
{
base.Destroy(mode);
}
}
}
我们把算好的电力赋值给电力交换的组件,就是这么简单。然后我们打包成dll测试一下。完成这一步,我们的太阳能发电机已经可以供电了!
这里有一点需要注意一下,如果建筑的xml中<tickerType>Normal</tickerType>
的话,要重写RareTick()方法而不是Tick(),RareTick比Tick的频率慢250倍(老外说的)
1.3 绘制电量条
我们的发电机没有电量条看起来总是怪怪的,快拿起键盘画一个上去。
/// <summary>
/// 绘制方法
/// </summary>
public override void Draw()
{
base.Draw();
GenDraw.FillableBarRequest fbr = default(GenDraw.FillableBarRequest); //初始化一个填充条
fbr.center = DrawPos + Vector3.up * 0.1f;//绘制中心
fbr.size = new Vector2(2.3f, 0.14f);//尺寸
fbr.fillPercent = realPower / fullSunPower;//填充比例
fbr.filledMat = SolidColorMaterials.SimpleSolidColorMaterial(new Color(0.5f, 0.475f, 0.1f), false);//填充部分的颜色
fbr.unfilledMat = SolidColorMaterials.SimpleSolidColorMaterial(new Color(0.15f, 0.15f, 0.15f), false);//未填充部分的颜色
fbr.margin = 0.15f;//间距
Rot4 rot4 = Rotation;//旋转
rot4.Rotate(RotationDirection.Clockwise);//调整一下素材旋转度
fbr.rotation = rot4;
GenDraw.DrawFillableBar(fbr);//出来吧电量条!
}
效果以假乱真
源码链接
如果这篇文章对你有帮助,点赞收藏支持一下呗!