[Unity] Script _laser system / 유니티 스크립트 레이져 시스템

2019. 8. 9. 18:21unity/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;
        }
    }
}