Announcement

Collapse
No announcement yet.

[Video][Code] Make Materials Change Color by Player Velocity, Material Instances

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    [Video][Code] Make Materials Change Color by Player Velocity, Material Instances

    Dear Community,

    In this tutorial I provide you with the code to make materials on objects in your game world respond to the velocity of the player pawn / Kactor. See video below for some entertaining examples!

    You can use the essence of what I am showing you here to control many aspects of your materials directly with unrealscript to cause effects like

    1. Materials change color as the percent of enemies left alive changes

    2. A 3d material indicator reports the player health or ammo levels by changing color or switching between several different textures or brightness levels

    3. walls “light up” by changing their brightness levels when the player unit touches them, etc!


    The way this is done is through what are called Material Instances



    ~~~

    How the Whole Screen Rainbow Effect Works

    Whole Screen Effects, Material Effects in Post Process Chain

    ~~~

    How to Make Materials Respond to Player Velocity

    Step 1: You must create your Material and convert some of its values into parameters.

    Here's a great tutorial on the basics of this process.

    ~~~

    Material That Changes Color In Response to Player Unit Velocity

    Here's the entire material code for the color changing material from my video:

    Sine period = 0.3
    Fresnel power = 2
    Lerp = LinearInterpolate




    ~~~

    Unreal Script Code

    Step 2: Here's the code you need to add to your player pawn / kactor or other creature class so that the velocity of your actor affects the material's color.

    Code:
    //Put this code in your pawn or KActor
    //or other PLayer OBJ class
    //Or you could put this in the enemy
    //pawn / creature class if you want 
    //to have the world's materials 
    //respond to their velocity.
    
    //Velocity Aware Materials
    var MaterialInstance velocityColorResponder;
    
    Simulated Event PostBeginPlay() {
    	
    	super.postbeginplay();
    	
    	//set gameinfo, this is my game info
    	//enables me to access the instance of this class
    	//after game starts 
    	CustomGameInfo(WorldInfo.Game).playerOBJ = Self;
    	
    	//======== Velocity Sensitive Materials ============================
    	velocityColorResponder = new Class'MaterialInstanceConstant';
    	velocityColorResponder.SetParent(
    		Material'YourPackage.Materials.VelocityColorResponder');
    }
    
    Simulated Function tick( Float DeltaTime ) {
    	
    	local vector unitVelocity;
    	local vector unitVelocityNormalized;
    	local LinearColor unitVelocityColor;
    	
    	//very important line
    	super.tick(deltatime);
    	
    	//~~~~~
    	//gets velocity of this class instance (Self)
    	unitVelocity = StaticMeshComponent.GetRootBodyInstance().GetUnrealWorldVelocity();
    	
    	//normalized velocity
    	//remove unitVelocity var for efficiency
    	//kept for clarity
    	unitVelocityNormalized = Normal(unitVelocity);
    	
            //Velocity Normalized or else the colors get waay
            //too bright (my player unit can have vel. of 1100+
    	unitVelocityColor.R = unitVelocityNormalized.x; 
    	unitVelocityColor.G = unitVelocityNormalized.y;
    	unitVelocityColor.B = unitVelocityNormalized.z;
    	unitVelocityColor.A = 1;
    	//you could use alpha channel to store
    	//additional useful info for your material instance
    	
    	//color responder
    	velocityColorResponder.SetVectorParameterValue('Color', unitVelocityColor);
    }
    ~~~

    Interpolate Between Two Textures Based On Normalized Player Velocity

    Controlling the Speed of the Lightning Effect on the Material

    Motion_4wayChaos is a Material function, use the lower search bar
    Same for the VectorToRadialValue function


    Please note that the 4wayChaos will ONLY take in a TextureObject, took me awhile to understand this.

    I am using my own custom black/white mask of size 1024x1024 as the texture, you can easily make your own or find a mask in the UDK.



    Converting Vector To Angle Via Material Function

    Notice how the Lerp / LinearInterpolator has A and B as a Red and Black, and a Pink and Dark Blue texture respectively.

    The alpha channel of the Lerp is used to decide how much of A and B to show.

    The Velocity function is converting the parameterized input into an angle for us, and this is the alpha of the Lerp.

    Notice the Velocity function can also retrieve the distance value of the input parameter.



    Code:
    //Velocity Aware Materials
    var MaterialInstance velocityResponder;
    
    Simulated Event PostBeginPlay() {
    	
    	super.postbeginplay();
    	
    	//set gameinfo, this is my game info
    	//enables me to access the instance of this class
    	//after game starts 
    	CustomGameInfo(WorldInfo.Game).playerOBJ = Self;
    	
    	//======== Velocity Sensitive Materials ============================
    	velocityResponder = new Class'MaterialInstanceConstant';
    	velocityResponder.SetParent(
    		Material'YourPackage.Materials.VelocityResponder');
    	velocityResponder.SetScalarParameterValue('Speed', 1);
    }
    
    Simulated Function tick( Float DeltaTime ) {
    	
    	local vector unitVelocity;
    	local vector unitVelocityNormalized;
    	local LinearColor unitVelocityColor;
    	
    	//very important line
    	super.tick(deltatime);
    	
    	//~~~~~
    	//gets velocity of this class instance (Self)
    	unitVelocity = StaticMeshComponent.GetRootBodyInstance().GetUnrealWorldVelocity();
    	
    	//normalized velocity
    	unitVelocityNormalized = Normal(unitVelocity);
    	
            //Velocity Normalized or else the colors get waay
            //too bright (my player unit can have vel. of 1100+
    	unitVelocityColor.R = unitVelocityNormalized.x; 
    	unitVelocityColor.G = unitVelocityNormalized.y;
    	unitVelocityColor.B = unitVelocityNormalized.z;
    	unitVelocityColor.A = 1;
    	//you could use alpha channel to store
    	//additional useful info for your material instance
    	
    	//velocity and direction responder
    	VelocityResponder.SetVectorParameterValue('Velocity', unitVelocityColor );
    	velocityResponder.SetScalarParameterValue('Speed', 
    	   vsize(unitVelocity) / 700);
    }
    ~~~

    How to Parameterize The Sine Function's Period
    How to control / expose the material sine function period value with unrealscript code

    This took me awhile to figure out! I hope you can make use of this in your own material instance adventures!



    ~~~

    Summary

    Use the basic process I am showing you here to cause your game world to come to life in response to player actions or other aspects of your game's gameplay!

    So many possibilities!

    Enjoy!



    Rama

    #2
    Bump for community, material instances are awesome

    Rama

    Comment


      #3
      Thank you very much evernewjoy, this is a really cool tutorial!

      I have a question, since I'm working on something that is similar:
      I'm dealing with kismet/matinee because I'm not a programmer, and I'm doing something like this:
      I have a character, I'm switching the Scalar Parameter on the material to change it, but as far as I saw I can change the Scalar Parameter via Matinee by keying the values directly in there, or ( a bit more complex ) by setting up Kismet and performing the material change by "Event > button press"
      What I would like to do is:
      Based on a bone location of the character ( lets say the pelvis ) I would like the Scalar Parameter to change whenever he lift up the left shoulder.
      So if I have an animation of the charater where he lift up his left shoulder, the material automatically updates itself by driving the Scalar Parameter ( that I wanto to drive based on the relative distance between the pelvis bone and the clavicle bone ), so no manual values inside Matinee, but everything would update automatically.

      I'm trying to use sockets and bones themself, but I have no idea how to access location information for specific bones/socket, and maybe drive the scalar parameter by the distance between the pelvis of the character and the clavicle of the character ( so I would need relative location to the pelvis bone in order to do that )

      Do you know if is possible to do this with Kismet or it requires scripting?

      Cheers and thanks again for the tutorial

      Comment


        #4
        here's a thread about getting socket locations
        http://forums.epicgames.com/threads/...-another-class

        measuring the distance between them is easy

        Code:
        vsize(socket1.location - socket2.location)

        regarding not using unrealscript, if you cant find a kismet node for getting socket info, then you can make your own

        if you make your own you might as well just do all the stuff in the node and output the distance between the sockets.

        Your custom node can access your player pawn and use the first thread I wrote about to get the pawn's socket info.

        my tutorial on custom kismet nodes

        making custom kismet nodes is a great way for you to slowly use more and more unrealscript

        Rama

        PS: meaning you could make a node with a float output called "get player shoulder-pelvis dist" and just have all the code in that custom node, and then use that distance in rest of your kismet code

        PSS: if you dont want to use the gameinfo method to get your pawn, you could supply your player pawn as an input tot he node, as a kismet OBJECT var. And then cast it to a pawn, and retrieve socket info

        If you do use gameinfo method you need to make sure your gameinfo is getting a reference to your pawn during postbeginplay() as I explain in this tutorial scroll down to part about connecting gameinfo and playercontroller

        Comment


          #5
          Thanks for the reply!
          Reading the kismet documentation I found out that the only node that will give you the location information is the "Get location and rotation" node, but is in world space, and is not what I'm looking for.

          I'm thinking that the best solution for me would be to drive the scalar parameter on the material instance by the distance between a "fixed bone/sockets" ( as I said, the pelvis ) and another bone/socket ( the clavicle )....I guess that the best way would be to place sockets, because they're easier to get the info, right?

          So I was thinking to get a Min-Max value based on those distances, then convert the min value to 0 and the max value to 1, so the distance will be converted in realtime into a specific value, that is a range between 0 and 1, that will drive the scalar parameter amount.
          Based on what I'm looking for I think that this is the best solution so far.

          I'm watching some videotutorials on how to create a custom kismet node and your tutorial, so I think it is time for me to learn a bit of Unrealscript...I know exactly what I'm looking for, but is the "how" that is not so clear, but I guess it would be trial and error

          Anyway thank you very much for the tips, and if you have any other advices it'll be great

          Comment


            #6
            Maybe I found the solution in Kismet! But I'm not sure how to do some things...let me describe you what I'm doing:

            Basically I have two InterpActor in my scene, one of them moves, so as soon as I start the level the animation on one of them starts.

            What I'm doing right now is using the "Get Distance" node that gives me a float value; I would like to convert the initial distance between them ( so as soon as the animation starts ) from UU value to be my 0 value.
            Then I would like to set the final distance between them ( when the animation finishes ) to be converted as a value of 1. ( by the way I saw that you can use sockets as well to be inputs ).

            By doing this I will have a Min-Max value of the animation that gives me values between 0 and 1. Basically Kismet will read the distance in realtime, and give me a value that would be converted between the Min-Max value ( so between 0 and 1 )
            , so if I apply another animation to the InterpActor ( lets say they move between the Min-Max at different distances each time ) Kismet would give a value between 0 and 1 always in realtime.

            As far as setup the get distance node and plug in the two InterpActor/Sockets into the node is simple, but then I don't know how to convert the float value into a value as I described above.

            When I have a value converted in realtime between 0 and 1 I would like to have that value to drive the "Material Instance > Set ScalarParam " value, in order for the material to be updated in realtime during the animation

            Any suggestions?

            Comment


              #7
              okay let's suppose:

              //distance between two objects at level start
              startingDistance = 500;

              //max separation distance
              finalDistance = 2000;


              To modulate this to a value between 0 and 1 you do this math

              Code:
              //curDistance must be something between starting and final
              0 to 1 value = (curDistance - startingDistance) / (finalDistance - startingDistance)
              so in this example

              500 will yield 0
              2000 will yield 1

              and 1250 will yield 0.5




              you can make this in kismet easily cause its all simple math

              just watch those parenthesis

              or make a custom node that accepts input for min and max range and current value and outputs 0 to 1

              that'd be better for your coding practice

              it's much easier to be careful about the parenthesis if you just code a node yourself

              here's the links and vars you'd need to get you started, do fill in the rest

              Code:
              //fill in class info here
              
              var float minRange;
              var float maxRange;
              var float curvalue;
              var float outputvalue;
              
              //fill in the rest, see my tutorial on kismet nodes
              
              defaultproperties
              {
               VariableLinks(0)=(ExpectedType=class'SeqVar_Float',
                 LinkDesc="Min",PropertyName=minRange)
              
               VariableLinks(1)=(ExpectedType = class'SeqVar_Float', 
                 LinkDesc = "Max", PropertyName = maxRange)
              
               VariableLinks(2)=(ExpectedType = class'SeqVar_Float', 
                 LinkDesc = "Value", PropertyName = curValue)
              
              VariableLinks(3)=(ExpectedType=class'SeqVar_Float',LinkDesc="0 to 1 value",
                 bWriteable=TRUE,
                 bSequenceNeverReadsOnlyWritesToThisVar=TRUE,
                 PropertyName=outputValue)
              }
              Rama


              PS: Advanced: in your kismet node do a check to make sure the input value is between the min and max, or else you will get negative results and crazyness

              also make sure the range inputted is not 0, or you will get a divide by 0 error

              do these checks after the basics are done

              Comment


                #8
                DOesnt seem to be working for me.

                In my pawn class
                Code:
                //Searer Burn
                var MaterialInstance SBurn;
                //
                var	float SBurnTime;
                
                simulated function PostBeginPlay( )
                {
                
                	Super.PostBeginPlay( );
                	SBurn = new Class'MaterialInstanceConstant';
                	SBurn.SetParent(Material'SL_CH_ALL.ParentMatsTexs.CHParent_Mat');
                	
                }
                
                function PlayHit(float Damage, Controller InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, TraceHitInfo HitInfo)
                {
                	
                	local class<SLDamageType> SLDamage;
                *Snipped*
                
                	
                	if ( SLDamage.default.bCausesSearerBurn )
                				{
                				SBurn.SetScalarParameterValue('SearerBurn', 3);	
                				SetTimer(SBurnTime, false, SBurn.SetScalarParameterValue('SearerBurn', 1));
                
                				}
                	Super.PlayHit( Damage, InstigatedBy, HitLocation, damageType, Momentum, HitInfo );
                
                
                
                }
                
                defaultproperties
                {
                
                
                *Snipped*
                  
                 
                 SBurnTime = 1.2f
                  
                }

                In my DamageType Class:

                Code:
                var bool bCausesSearerBurn;
                
                defaultproperties
                {
                
                	bCausesSearerBurn=True
                }

                Comment


                  #9
                  Code:
                  SetTimer(SBurnTime, false, SBurn.SetScalarParameterValue('SearerBurn', 1));
                  You cannot have statements inside the SetTimer functions (or any function for that matter). What you need is a function's name:

                  Code:
                  Function SetSearerBurn()
                  {
                  	SBurn.SetScalarParameterValue('SearerBurn', 1);
                  }
                  
                  ...
                  SetTimer(SBurnTime, false, NameOf(SetSearerBurn));

                  Comment


                    #10
                    Oic. Thunks. Just realized I called my parameter searer death as opposed to searer burn

                    Comment


                      #11
                      Sorry still no joy. I should add a log

                      Comment


                        #12
                        I like the fact that you add so many tutorials but I gotta say toning down on the acid trip materials would help a bit, tried watching the video and got a headache after the first 30 secs.

                        Comment


                          #13
                          Ok my function is being called (I added a log world broadcast message). But he material is just not showing the changes.

                          Comment


                            #14
                            WOW This is amazing! Thank you so much evernewjoy and others!

                            Comment


                              #15
                              Interesting tutorial , it's kinda cool to watch it together with this https://www.youtube.com/watch?v=3oRKvpZ7PjE you could have used this as backround : P.
                              Gives a lot of idea on how you can treat the MICS. .

                              For example the fire ball of a catapult , would have different color depending on the speed, since the fire would have more oxygen .

                              Comment

                              Working...
                              X