2019. 8. 9. 18:21ㆍunity/unity script
in unreal engine default offered laser system, but in unity has no laser system
lately, we need laset system, so decided to create lower version
laset system define
1. conect 2 point(player & enemy)
2. possible to move on runtime(game play)
3. able to select system as texture tiling or scaling
we will creating script look like this image
conect point1 to 2
move able on runtime or editormode
texture tiling mode
usually, used in beam style
texture scaling mode
usually, used in image sequence style
we need material, script, prefab object for creating this system
/////////////////////prefab object
I offered default prefab object to 'effect designer'
it had a fixed hierarchy, don't have to change
top level parent include actually execute logic about laser system
base_pos as meaning laser startING point
target_pos as meaning endpoint
beam root is parent object controlled by'beam script'
You can set the axis offset in the 'transform object' under the beam_root
- this script using 'look at method' offered in unity3d, the method's rotation axis is fixed
So if the axis used is different from the actual axis,
you will need an intermediate object that needs to be interpolated.
/////////////////////material(shader)
material inspector
set texture's style in material
set y tile or xtile if texture is tiling texture
x tile is only used to create blocks of textures, like the atlas system
speed - texture animating speed
num - texture sheet starting number
repet_x - it use in script about how many times repeats in default range
this script set default range for best visual
If the default range is 4, the texture will repeat by repeat_x on scale 4
this shader is shown how controlling textures 'uv'
Carefully look at how basic tiling and iteration x are connected.
original image
repeat_x = 2.25
repeat_x = 1
repeat_x = 2.25 tile x = 2
looks like the image of repeatx = 1 but uses only half of the original image
//shader code
Shader "Fx_textureanimation"
{
Properties
{
_Texture("Texture", 2D) = "white" {}
_xtile("xtile", Float) = 0
_ytile("ytile", Float) = 0
_num("num", Float) = 0
_repeat_X("repeat_X", Float) = 0
_speed("speed", Float) = 0
[HideInInspector] _texcoord( "", 2D ) = "white" {}
[HideInInspector] __dirty( "", Int ) = 1
}
SubShader
{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent+0" "IgnoreProjector" = "True" "IsEmissive" = "true" }
Cull Off
CGINCLUDE
#include "UnityShaderVariables.cginc"
#include "UnityPBSLighting.cginc"
#include "Lighting.cginc"
#pragma target 3.0
struct Input
{
float2 uv_texcoord;
};
uniform sampler2D _Texture;
uniform float _repeat_X;
uniform float _xtile;
uniform float _ytile;
uniform float _speed;
uniform float _num;
inline half4 LightingUnlit( SurfaceOutput s, half3 lightDir, half atten )
{
return half4 ( 0, 0, 0, s.Alpha );
}
void surf( Input i , inout SurfaceOutput o )
{
float temp_output_4_0 = ( 1.0 / _xtile );
float temp_output_5_0 = ( 1.0 / _ytile );
float2 appendResult15 = (float2(( _repeat_X * temp_output_4_0 ) , temp_output_5_0));
float mulTime25 = _Time.y * _speed;
float temp_output_21_0 = ( mulTime25 + _num );
float2 appendResult8 = (float2(( floor( temp_output_21_0 ) * temp_output_4_0 ) , ( temp_output_5_0 * floor( ( temp_output_21_0 / _xtile ) ) )));
float2 uv_TexCoord3 = i.uv_texcoord * appendResult15 + appendResult8;
float4 tex2DNode1 = tex2D( _Texture, uv_TexCoord3 );
o.Emission = tex2DNode1.rgb;
o.Alpha = tex2DNode1.a;
}
ENDCG
CGPROGRAM
#pragma surface surf Unlit alpha:fade keepalpha fullforwardshadows
ENDCG
Pass
{
Name "ShadowCaster"
Tags{ "LightMode" = "ShadowCaster" }
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#pragma multi_compile_shadowcaster
#pragma multi_compile UNITY_PASS_SHADOWCASTER
#pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2
#include "HLSLSupport.cginc"
#if ( SHADER_API_D3D11 || SHADER_API_GLCORE || SHADER_API_GLES || SHADER_API_GLES3 || SHADER_API_METAL || SHADER_API_VULKAN )
#define CAN_SKIP_VPOS
#endif
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
sampler3D _DitherMaskLOD;
struct v2f
{
V2F_SHADOW_CASTER;
float2 customPack1 : TEXCOORD1;
float3 worldPos : TEXCOORD2;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata_full v )
{
v2f o;
UNITY_SETUP_INSTANCE_ID( v );
UNITY_INITIALIZE_OUTPUT( v2f, o );
UNITY_TRANSFER_INSTANCE_ID( v, o );
Input customInputData;
float3 worldPos = mul( unity_ObjectToWorld, v.vertex ).xyz;
half3 worldNormal = UnityObjectToWorldNormal( v.normal );
o.customPack1.xy = customInputData.uv_texcoord;
o.customPack1.xy = v.texcoord;
o.worldPos = worldPos;
TRANSFER_SHADOW_CASTER_NORMALOFFSET( o )
return o;
}
half4 frag( v2f IN
#if !defined( CAN_SKIP_VPOS )
, UNITY_VPOS_TYPE vpos : VPOS
#endif
) : SV_Target
{
UNITY_SETUP_INSTANCE_ID( IN );
Input surfIN;
UNITY_INITIALIZE_OUTPUT( Input, surfIN );
surfIN.uv_texcoord = IN.customPack1.xy;
float3 worldPos = IN.worldPos;
half3 worldViewDir = normalize( UnityWorldSpaceViewDir( worldPos ) );
SurfaceOutput o;
UNITY_INITIALIZE_OUTPUT( SurfaceOutput, o )
surf( surfIN, o );
#if defined( CAN_SKIP_VPOS )
float2 vpos = IN.pos;
#endif
half alphaRef = tex3D( _DitherMaskLOD, float3( vpos.xy * 0.25, o.Alpha * 0.9375 ) ).a;
clip( alphaRef - 0.01 );
SHADOW_CASTER_FRAGMENT( IN )
}
ENDCG
}
}
Fallback "Diffuse"
CustomEditor "ASEMaterialInspector"
}
//////////// ///////script
we prepare prefab and materials
Now, we will rotate and scale the beam_root of the prefab so that it looks like a laser
a lot of variables shown, but many of variables only usage in debugging, so disappear later
beamdistance - auto calculated in the script(it was used in beam texture base)
meaning distance between base_pos(starpoint) to target_pos(endpoint)
beamtexturebase
This is what we mentioned in describing the default range of material repeat_x
if it is 4 and repeat_x is 2
when the distance between the strt_pos and target_pos is 4( mached default setting )
texture shown 2 times(repeat 2 time)
this function is useful for animated sequence image work
isrepeat - it is check point about texture is repeat or scale
life time - it is beam's lifetime
other variables are not used in system almost there is using in debug or viewing created laser effect
///////////////////// code explane
we check out some point about main features of the laser system
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode] //<<using in editormode
public class Fx_beam_test_C : MonoBehaviour
{
string baseobject = "Base_pos";
string targetobject = "Target_Pos";
string beamobject = "Beam_Root";
public Vector3 LookatUpvector = new Vector3(0, 0, 0);
Vector3 basepos = new Vector3(0, 0, 0);
Vector3 targetpos = new Vector3(0, 0, 0);
Transform beamobjectTrans;
//texture transform parameter //<<variables in this block is for transform
public float beamdistance = 1;
public float beamdTextureBase = 1;
public bool isrepeat = false;
public float beamlifetime = 0;
public float BeamActiveDelay = 0;
public float currentlifetime;
// material parameter //<<variables for material control
public float materialrepeatbase = 1;
Material beammaterial;
Material beammaterialIns;
string beambody = "Beam_Body_quad";
// editor only paramter(remove rater)
public float viewdelay_editor = 1;
public float Tcurrentlifetime = 0;
private void OnEnable()
//<<base variables setting block it is use in editormode
//<<if dont have to cosider 'excuteeditormode', this block move to 'void start()'
//<<Nothing special, but fill each variable according to the predefined content.
{
basepos = gameObject.transform.Find(baseobject).position;
gameObject.transform.Find(beamobject).position = basepos;
beamobjectTrans = gameObject.transform.Find(beamobject);
// Set material parameter
var tempobjectP = beamobjectTrans.transform.Find("transformobject");
var tempobject = tempobjectP.transform.Find(beambody);
beammaterial = tempobject.GetComponent().sharedMaterial;
beammaterialIns = beammaterial;
//materialrepeatbase = beammaterial.GetFloat("_repeat_X");
//lifetime reset
//start setup
beamobjectTrans.gameObject.SetActive(false);
}
private void OnDisable() //<<reset action
{
Tcurrentlifetime = 0;
beammaterial = beammaterialIns;
}
void Start()
{
}
void Update()
{
Tcurrentlifetime += Time.deltaTime;//시간 더해줌
//<<'Tcurrentlifetime' is used to force ticking for animation time move to forward
if (beamlifetime == 0)
{
if (BeamActiveDelay <= Tcurrentlifetime)
{
beamobjectTrans.gameObject.SetActive(true);
beambodyFN();
currentlifetime = 0;
}
else
return;
}
//<< this block is based for laser action
//<<you will find similar code lines when shown other blocks(check condition > run beambodyFn() > reset)
//<<it was checked block's state, apply according to prearranged conditions
//<<find correct state using 'if / else' statement
//<<'beambodyFN()' is includ all actions of laser
//<< 'void update ()' only finds the exact time the laser action will be executed
else
{
currentlifetime += Time.deltaTime;//시간을 계속 더해줌
if (currentlifetime <= beamlifetime + BeamActiveDelay)
{
if (BeamActiveDelay <= Tcurrentlifetime)
{
beamobjectTrans.gameObject.SetActive(true);
beambodyFN();
}
else
return;
}
else if (currentlifetime <= beamlifetime + BeamActiveDelay + viewdelay_editor)
{
beamobjectTrans.gameObject.SetActive(false);
}
else
{
if (BeamActiveDelay <= Tcurrentlifetime)
{
//beamobjectTrans.gameObject.SetActive(true);
beambodyFN();
currentlifetime = 0;
Tcurrentlifetime = 0;
}
else
return;
}
}
}
//<< 'void update()' block's main feature compares timeline for delay, repeat, and editor work
//<< it was don't necessary for built game, just working on the assets creating situations
//<<now, lower blocks is including main feature for laser work
//<<the work looks at the target, scaling laser body, matching texture tiling
void beambodyFN()
{
targetpos = gameObject.transform.Find(targetobject).position;
//<<get target's transform, the target was positioned something locations by game logic
beamdistance = Vector3.Distance(basepos, targetpos);
//<< Calculate distance between target and player, that is using in tiling texture
beamobjectTrans.localScale = new Vector3(1, 1, beamdistance);
//<<matching beambody's size to beam distance, the mesh used for the beambody is 1 in size
if your mesh size is not 1, you multiply or divide 'beam distance'
beamobjectTrans.LookAt(targetpos);
//<rotate axis of beambody by 'lookat' function
unity offered 'lookat' function is locked axis so we made transformobject in laser prefab
if (beamdistance < beamdTextureBase)
{
beammaterialIns.SetFloat("_repeat_X", (beamdistance / beamdTextureBase) * materialrepeatbase);
}
//<Forces the texture to repeat when lower than the minimum size(=beamdtexturebase)
else
{
if (isrepeat == true)
{
beammaterialIns.SetFloat("_repeat_X", (beamdistance / beamdTextureBase) * materialrepeatbase);
}
else
//< this block is controling texture repeat or scale
simply//< this block is controlling texture repeat or scale
it's not just multiplied distance/texture base, set minimum and convert scale texture to repeat
when lower than minimum size
return;
}
}
}
it is simple scripts looks like laser system
but useful for casual style game and that had many points about extend or upgrade
/////////////////////full code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class Fx_beam_test_C : MonoBehaviour
{
string baseobject = "Base_pos";
string targetobject = "Target_Pos";
string beamobject = "Beam_Root";
public Vector3 LookatUpvector = new Vector3(0, 0, 0);
Vector3 basepos = new Vector3(0, 0, 0);
Vector3 targetpos = new Vector3(0, 0, 0);
Transform beamobjectTrans;
//texture transform parameter
public float beamdistance = 1;
public float beamdTextureBase = 1;
public bool isrepeat = false;
public float beamlifetime = 0;
public float BeamActiveDelay = 0;
public float currentlifetime;
// material parameter
public float materialrepeatbase = 1;
Material beammaterial;
Material beammaterialIns;
//public Transform tempobject;
//public Transform tempobjectP;
string beambody = "Beam_Body_quad";
// editor only paramter(remove rater)
public float viewdelay_editor = 1;
public float Tcurrentlifetime = 0;
private void OnEnable()
{
basepos = gameObject.transform.Find(baseobject).position;
//targetpos = gameObject.transform.Find(targetobject).localPosition;
gameObject.transform.Find(beamobject).position = basepos;
beamobjectTrans = gameObject.transform.Find(beamobject);
var tempobjectP = beamobjectTrans.transform.Find("transformobject");
var tempobject = tempobjectP.transform.Find(beambody);
beammaterial = tempobject.GetComponent().sharedMaterial;
beammaterialIns = beammaterial;
//start setup
beamobjectTrans.gameObject.SetActive(false);
}
private void OnDisable()
{
Tcurrentlifetime = 0;
beammaterial = beammaterialIns;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Tcurrentlifetime += Time.deltaTime;//시간 더해줌
if (beamlifetime == 0)
{
if (BeamActiveDelay <= Tcurrentlifetime)
{
beamobjectTrans.gameObject.SetActive(true);
beambodyFN();
currentlifetime = 0;
}
else
return;
}
else
{
currentlifetime += Time.deltaTime;//시간을 계속 더해줌
if (currentlifetime <= beamlifetime + BeamActiveDelay)
{
if (BeamActiveDelay <= Tcurrentlifetime)
{
beamobjectTrans.gameObject.SetActive(true);
beambodyFN();
}
else
return;
}
else if (currentlifetime <= beamlifetime + BeamActiveDelay + viewdelay_editor)
{
beamobjectTrans.gameObject.SetActive(false);
}
else
{
if (BeamActiveDelay <= Tcurrentlifetime)
{
//beamobjectTrans.gameObject.SetActive(true);
beambodyFN();
currentlifetime = 0;
Tcurrentlifetime = 0;
}
else
return;
}
}
}
void beambodyFN()
{
targetpos = gameObject.transform.Find(targetobject).position;
beamdistance = Vector3.Distance(basepos, targetpos);
beamobjectTrans.localScale = new Vector3(1, 1, beamdistance);
beamobjectTrans.LookAt(targetpos);
//--------material / texture setting
if (beamdistance < beamdTextureBase)
{
beammaterialIns.SetFloat("_repeat_X", (beamdistance / beamdTextureBase) * materialrepeatbase);
}
else
{
if (isrepeat == true)
{
beammaterialIns.SetFloat("_repeat_X", (beamdistance / beamdTextureBase) * materialrepeatbase);
}
else
return;
}
}
}