Topic: (Source) Advanced Tutorial: Hit trails using XWeaponTrail
In this tutorial, I'll show you how to mod in XWeaponTrail to add hit trails to your moves. You will need to install the XWeaponTrail asset before starting, and make sure you have version 1.5 of UFE.
This mod is a fairly involved, and will also require some changes to how you'd normally setup your XWeaponTrail particles (see the final section under all the code changes).
As always, post below if you get stuck or if I missed anything.
------
In MoveSetScript.cs, look for:
public class ParticleInfo:ICloneable {
and inside this class under the bodypart declaration, add in:
// X-Weapon Trail support
public bool isWeaponTrail;
public BodyPart bodyPartEnd; // This is X-Weapon's EndPoint; bodypart is StartPoint
------
Then create a new script called StopParticleDelay.cs. We need this to allow for better control of stopping the XWeaponTrail particle. Replace the default code generated with the below code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class StopParticleDelay : MonoBehaviour {
// Script that uses delay to stop a particle smoothly (instead of Destroy)
public float stopDelay;
private float startTime;
private ParticleSystem[] myParticleSystems;
private Xft.XWeaponTrail myWeaponTrail;
// Use this for initialization
void Start () {
myParticleSystems = GetComponentsInChildren<ParticleSystem>();
myWeaponTrail = GetComponent<Xft.XWeaponTrail>();
startTime = Time.timeSinceLevelLoad;
}
void FixedUpdate () {
if (Time.timeSinceLevelLoad >= startTime + stopDelay) {
foreach(ParticleSystem system in myParticleSystems) {
system.Stop(true);
}
if (myWeaponTrail != null) {
myWeaponTrail.StopSmoothly(0.2f);
}
Destroy(gameObject, 2.0f);
}
}
public IEnumerable DelayedStop(float delay) {
Debug.Log ("Starting DelayedStop " + Time.timeSinceLevelLoad);
yield return new WaitForSeconds(delay);
foreach(ParticleSystem system in myParticleSystems) {
system.Stop(true);
Debug.Log ("Stopping Particle " + system.name + ", " + Time.timeSinceLevelLoad);
}
if (myWeaponTrail != null) {
myWeaponTrail.StopSmoothly(0.5f);
}
Debug.Log ("Destroying GO " + Time.timeSinceLevelLoad);
Destroy(gameObject, 2.0f);
}
}
------
Then in ControlsScript.cs, look for:
// Check Particle Effects
foreach (MoveParticleEffect particleEffect in move.particleEffects){
and replace the foreach loop with the below:
// Check Particle Effects
foreach (MoveParticleEffect particleEffect in move.particleEffects){
if (
!particleEffect.casted &&
particleEffect.particleEffect.prefab != null &&
move.currentFrame >= particleEffect.castingFrame
){
particleEffect.casted = true;
Vector3 newPosition = myHitBoxesScript.GetPosition(particleEffect.particleEffect.bodyPart);
GameObject pTemp;
if (particleEffect.particleEffect.isWeaponTrail) {
pTemp = (GameObject) Instantiate(particleEffect.particleEffect.prefab,
newPosition,
Quaternion.identity);
Vector3 endPosition = myHitBoxesScript.GetPosition(particleEffect.particleEffect.bodyPartEnd);
pTemp.transform.parent = myHitBoxesScript.GetTransform(particleEffect.particleEffect.bodyPart);
Xft.XWeaponTrail xComp = pTemp.GetComponent<Xft.XWeaponTrail>();
xComp.PointStart.position = newPosition;
xComp.PointEnd.position = endPosition;
} else {
newPosition.x += particleEffect.particleEffect.offSet.x * -mirror;
newPosition.y += particleEffect.particleEffect.offSet.y;
newPosition.z += particleEffect.particleEffect.offSet.z;
pTemp = (GameObject) Instantiate(particleEffect.particleEffect.prefab,
newPosition,
Quaternion.identity);
if (particleEffect.particleEffect.stick) pTemp.transform.parent = myHitBoxesScript.GetTransform(particleEffect.particleEffect.bodyPart);
}
StopParticleDelay spComp = pTemp.GetComponent<StopParticleDelay>();
if (spComp != null) {
spComp.stopDelay = particleEffect.particleEffect.duration;
} else {
Destroy(pTemp, particleEffect.particleEffect.duration);
}
}
}
------
Then to add it to the Move Editor, open MoveEditorWindow.cs and look for:
for (int i = 0; i < moveInfo.particleEffects.Length; i ++){
and replace this for loop with the below code:
for (int i = 0; i < moveInfo.particleEffects.Length; i ++){
EditorGUILayout.Space();
EditorGUILayout.BeginVertical(arrayElementStyle);{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();{
moveInfo.particleEffects[i].castingFrame = EditorGUILayout.IntSlider("Casting Frame:", moveInfo.particleEffects[i].castingFrame, 0, moveInfo.totalFrames);
if (GUILayout.Button("", "PaneOptions")){
PaneOptions<MoveParticleEffect>(moveInfo.particleEffects, moveInfo.particleEffects[i], delegate (MoveParticleEffect[] newElement) { moveInfo.particleEffects = newElement; });
}
}EditorGUILayout.EndHorizontal();
if (moveInfo.particleEffects[i].particleEffect == null) moveInfo.particleEffects[i].particleEffect = new ParticleInfo();
moveInfo.particleEffects[i].particleEffect.isWeaponTrail = EditorGUILayout.Toggle("X Weapon Trail:", moveInfo.particleEffects[i].particleEffect.isWeaponTrail, toggleStyle);
if (moveInfo.particleEffects[i].particleEffect.isWeaponTrail) {
moveInfo.particleEffects[i].particleEffect.prefab = (GameObject) EditorGUILayout.ObjectField("X-Weapon Trail Prefab:", moveInfo.particleEffects[i].particleEffect.prefab, typeof(UnityEngine.GameObject), true);
moveInfo.particleEffects[i].particleEffect.duration = EditorGUILayout.FloatField("Duration (seconds):", moveInfo.particleEffects[i].particleEffect.duration);
EditorGUI.BeginDisabledGroup(moveInfo.particleEffects[i].particleEffect.isWeaponTrail);{
moveInfo.particleEffects[i].particleEffect.stick = EditorGUILayout.Toggle("Sticky", true, toggleStyle);
}EditorGUI.EndDisabledGroup();
moveInfo.particleEffects[i].particleEffect.bodyPart = (BodyPart) EditorGUILayout.EnumPopup("Inner Point:", moveInfo.particleEffects[i].particleEffect.bodyPart, enumStyle);
moveInfo.particleEffects[i].particleEffect.bodyPartEnd = (BodyPart) EditorGUILayout.EnumPopup("Outer Point:", moveInfo.particleEffects[i].particleEffect.bodyPartEnd, enumStyle);
} else {
moveInfo.particleEffects[i].particleEffect.prefab = (GameObject) EditorGUILayout.ObjectField("Particle Effect:", moveInfo.particleEffects[i].particleEffect.prefab, typeof(UnityEngine.GameObject), true);
moveInfo.particleEffects[i].particleEffect.duration = EditorGUILayout.FloatField("Duration (seconds):", moveInfo.particleEffects[i].particleEffect.duration);
moveInfo.particleEffects[i].particleEffect.stick = EditorGUILayout.Toggle("Sticky", moveInfo.particleEffects[i].particleEffect.stick, toggleStyle);
moveInfo.particleEffects[i].particleEffect.bodyPart = (BodyPart) EditorGUILayout.EnumPopup("Body Part:", moveInfo.particleEffects[i].particleEffect.bodyPart, enumStyle);
moveInfo.particleEffects[i].particleEffect.offSet = EditorGUILayout.Vector3Field("Off Set (relative):", moveInfo.particleEffects[i].particleEffect.offSet);
}
EditorGUILayout.Space();
}EditorGUILayout.EndVertical();
}
------
Lastly, in XWeaponTrail.cs, to ensure the trail particle is destroyed change the Deactivate() method with the below:
public void Deactivate()
{
gameObject.SetActive(false);
if (mMeshObj != null) {
mMeshObj.SetActive(false);
Destroy(mMeshObj);
}
Destroy(gameObject);
}
=============
To make the XWeaponTrail prefab compatible, you'll need add the StopParticleDelay script to the gameobject. Also, set your trail's color here. In my example above I have two trails, one in white and another in pink.
You should also adjust your Max Frame, Granularity and FPS to suit your game. I've found higher Max Frame creates longer trails. In my image above the pink trail has Max Frame of 30, and white has 60.