1、目标
种植一棵橡树,从种子变成大树。
然后可以使用斧头砍伐橡树。
2、删除totalGrowthDays字段
修改growthDays的含义,定义每个值为到达当前阶段的累加天数。此时最后一个阶段就是totalGrowthDays的含义。所以就可以删除totalGrowthDays字段。
(1)修改SO_CropDetailsList的配置信息
修改前的GrowthDays的配置信息:
修改后的GrowthDays的配置信息:
每个值换成了累加的方式。
(2)修改CropDetails.cs脚本
删除totalGrowthDays字段。
(3)修改GridPropertiesManager.cs脚本
找出目前所处的成长阶段换成如下的代码:
// 找出目前所处的成长阶段
for (int i = growthStages - 1; i >= 0; i--)
{
if (gridPropertyDetails.growthDays >= cropDetails.growthDays[i])
{
currentGrowthStage = i;
break;
}
}
(3)修改GridCursor.cs脚本
Check if crop fully grown的逻辑修改如下:
3、修改CropStandard预制体的配置
修改CropSprite的Sprite Sort Point为Pivot。
修改CropHarvestedSprite的Sprite Sort Point为Pivot,Order in Layer为1。
调整完成后,收获的防风草会显示在Player之前,如下所示:
4、动画系统
已有的动画为:
查看TreeFallingLeft节点:
根级别的Sprite Renderer值为0,被禁用了。
子级别有Top和Trunk两个Sprite Renderer组件。
这就要求动画绑定的预设体有同样的结构。
5、创建橡树预设体
(1)创建CropTreeCanyonOak
在Hierarchy -> PersistentScene下新创建一个物体命名为CropTreeCanyonOak。
添加Box Collider 2D组件和Crop组件,相关配置信息如下:
(2)创建CropSprite
在CropTreeCanyonOak下创建子物体命名为CropSprite。
添加Sorting Group、Sprite Renderer、Animator组件,相应的配置信息如下:
双击Animator下的Controller(Tree),在Animation界面可以看到相关配置被黄色高亮显示,这表明animator识别不到相关的对象。
(3)创建Trunk
在CropSprite下创建物体命名为Trunk,添加组件信息如下:
Auto Tiling(自动贴片)的作用:要是你把 Box Collider 2D 组件添加到使用了瓦片精灵的 Sprite(精灵)上,并且开启了 Auto Tiling,这个组件就能够依据精灵的瓦片布局,自动对碰撞体的形状和大小进行调整。
- 贴合瓦片形状:它会按照精灵所使用的瓦片图集的边界,自动调整碰撞体的轮廓,让碰撞检测与精灵的视觉外观精准匹配。
- 处理多瓦片精灵:当精灵由多个瓦片组合而成时,Auto Tiling 能够自动生成一个覆盖所有瓦片的组合碰撞体,而无需你手动去调整每个瓦片的碰撞体。
- 提升效率:对于复杂的瓦片地图,开启 Auto Tiling 可以节省大量手动设置碰撞体的时间,避免逐个为瓦片添加碰撞体。
添加完成后,Animation下的Trunk立马置灰(正常状态)。
(4)创建Top
在CropSprite下创建子物体命名为Top,添加相关组件的配置信息如下:
添加完成后,Animation下的Top立马置灰(正常状态)。
(5)查看特效
点击CropSprite,点击Animation下的播放,可以看到特效。
(6)移动Prefab
将CropTreeCanyonOak从Hierarchy移动到Assets -> Prefabs -> Crop下。
并且删除Hierarchy下的CropTreeCanyonOak。
6、SO_CropDetailsList添加配置
因为Growth Prefab第5个Prefab已经有图片信息,所以Growth Sprite第5个不再需要图片信息。
7、运行游戏1
首先挖5块土,然后放入橡木种子。
走过未成熟的橡木时,树木会左右晃动。经过成熟的橡木后方时,会有褪色的效果。
接下来处理使用斧头砍伐成年橡树的逻辑。
8、修改GridCursor.cs脚本
修改IsCursorValidForTool函数,添加下面一行代码:
9、修改Player.cs脚本
在ProcessPlayerClickInput函数中添加一行代码:
在ProcessPlayerClickInputTool函数中,添加一个case如下:
case ItemType.Chopping_tool:
if (gridCursor.CursorPositionIsValid)
{
ChopInPlayerDirection(gridPropertyDetails, itemDetails, playerDirection);
}
break;
添加ChopInPlayerDirection函数如下:
private void ChopInPlayerDirection(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails, Vector3Int playerDirection)
{
// Trigger animation
StartCoroutine(ChopInPlayerDirectionRoutine(gridPropertyDetails, itemDetails, playerDirection));
}
添加ChopInPlayerDirectionRoutine函数如下:
private IEnumerator ChopInPlayerDirectionRoutine(GridPropertyDetails gridPropertyDetails, ItemDetails equippedItemDetails, Vector3Int playerDirection)
{
PlayerInputIsDisabled = true;
playerToolUseDisabled = true;
// Set tool animation to axe in override animation
toolCharacterAttribute.partVariantType = PartVariantType.axe;
characterAttributeCustomisationList.Clear();
characterAttributeCustomisationList.Add(toolCharacterAttribute);
animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);
ProcessCropWithEquippedItemInPlayerDirection(playerDirection, equippedItemDetails, gridPropertyDetails);
yield return useToolAnimationPause;
// After animation pause
yield return useToolAnimationPause;
PlayerInputIsDisabled = false;
playerToolUseDisabled = false;
}
修改ProcessCropWithEquippedItemInPlayerDirection如下,添加了对case ItemType.Chopping_tool的支持。
private void ProcessCropWithEquippedItemInPlayerDirection(Vector3Int playerDirection, ItemDetails equippedItemDetails, GridPropertyDetails gridPropertyDetails)
{
switch (equippedItemDetails.itemType)
{
case ItemType.Chopping_tool:
if(playerDirection == Vector3Int.right)
{
isUsingToolRight = true;
}
else if(playerDirection == Vector3Int.left)
{
isUsingToolLeft = true;
}
else if(playerDirection == Vector3Int.up)
{
isUsingToolUp = true;
}
else if(playerDirection == Vector3Int.down)
{
isUsingToolDown = true;
}
break;
case ItemType.Collecting_tool:
if (playerDirection == Vector3Int.right)
{
isPickingRight = true;
}
else if (playerDirection == Vector3Int.left)
{
isPickingLeft = true;
}
else if (playerDirection == Vector3Int.up)
{
isPickingUp = true;
}
else if (playerDirection == Vector3Int.down)
{
isPickingDown = true;
}
break;
case ItemType.none:
break;
}
// Get crop at cursor grid location
Crop crop = GridPropertiesManager.Instance.GetCropObjectAtGridLocation(gridPropertyDetails);
// Execute Process Tool Action For crop
if(crop != null)
{
switch (equippedItemDetails.itemType)
{
case ItemType.Chopping_tool:
crop.ProcessToolAction(equippedItemDetails, isUsingToolRight, isUsingToolLeft, isUsingToolDown, isUsingToolUp);
break;
case ItemType.Collecting_tool:
crop.ProcessToolAction(equippedItemDetails, isPickingRight, isPickingLeft, isPickingDown, isPickingUp);
break;
}
}
}
10、运行游戏2
存在一个问题:就是砍树的时候,用的不是斧头,而是锄头。这是因为使用了默认的Tool即Hoe。
之前,当玩家携带一个道具时,我们为动画创建了一些脚本对象资产,以便可以在携带道具和不携带之间切换。
现在,我们需要对use tool动画做类似的事情,以便在axe和hoe两个工具之间切换。
11、新建Animation Type
在Assets -> Scriptable Object Assets -> Animation -> Animation Types下新建tool目录,在其下再创建目录NoneAxe。
该目录右击,Create -> Scriptable Objects -> Animation -> Animation Type,重命名为ToolNoneAxeUseToolDown。
配置ToolNoneAxeUseToolDown的相关信息:
以同样的方式,分别创建ToolNoneAxeUseToolLeft、ToolNoneAxeUseToolRight、ToolNoneAxeUseToolUp。
比如ToolNoneAxeUseToolRight的参数如下:
在tool目录下再创建目录NoneHoe,相应地创建4个AnimationType,比如ToolNoneHoeUseToolDown如下:
在tool目录下再创建目录NoneScythe,相应地创建4个AnimationType,比如ToolNoneScytheSwingToolDown如下:
在tool目录下再创建目录NoneWateringCan,相应地创建4个AnimationType,比如ToolNoneWateringCanLiftToolUp如下:
新建完毕后,将上面16个添加到Player的CharacterCustomiser下:
添加完毕后,相应的数量从24变为40。
12、运行游戏3
再次砍树,发现正确使用了斧头这个工具