Download

Change material onclick

I have the following material:

f5af4351f92593c48d8639fd84afd3f1ce02bc3c.jpeg

I would like to change the value of IsSelected from 0 to 1 when an actor is clicked on.

I think I’m close. Here’s what I currently have:


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/Actor.h"
#include "VehicalSocketComponent.h"
#include "VehiclePart.generated.h"

UCLASS(Blueprintable)
class EDITOR3_API AVehiclePart : public AActor
{
	GENERATED_BODY()

public:

	// Sets default values for this actor's properties
	AVehiclePart();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	// Called every frame
	virtual void Tick(float DeltaSeconds) override;

	// Get energy drain
	virtual int32 GetEnergyContribution();

	// Get health regen
	virtual int32 GetHealthRegeneration();

protected:

	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Vehicle Part Properties")
		FName DisplayName = FName(TEXT("Unnamed Part"));

	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Vehicle Part Properties")
		int32 BlockCostToSpawn = 0;

	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Vehicle Part Properties")
		int32 CreditCostToBuy = 0;

	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Vehicle Part Properties")
		int32 CheaterCashCostToBuy = 0;

	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Vehicle Part Properties")
		int32 HealthPoints = 0;

	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Vehicle Part Properties")
		int32 DurabilityPoints = 0;

	UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "Mesh")
		UStaticMeshComponent * StaticMeshComponent;

private:

	UMaterial * Material;

	UMaterialInstanceDynamic * DynamicMaterial = nullptr;

	void OnConstruction(const FTransform &) override;

	void NotifyActorBeginCursorOver() override;

	void NotifyActorEndCursorOver() override;

	void NotifyActorOnClicked() override;

};


#include "Editor3.h"
#include "VehiclePart.h"

AVehiclePart::AVehiclePart()
{
	// Set this actor to call Tick() every frame
	PrimaryActorTick.bCanEverTick = true;

	// Create and attach the scene component to root
	RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root Component"));

	// Create and attach the static mesh component to our scene component
	StaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Static Mesh Component"));
	StaticMeshComponent->AttachTo(RootComponent);

	static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("Material'/Game/Materials/FancyPlastic.FancyPlastic'"));

	if (MaterialAsset.Succeeded()) Material = (UMaterial *) MaterialAsset.Object;

}

void AVehiclePart::OnConstruction(const FTransform & Transform)
{
	Super::OnConstruction(Transform);

	// Setup the dynamic material
	if (DynamicMaterial)
	{
		DynamicMaterial = UMaterialInstanceDynamic::Create(Material, this);
		StaticMeshComponent->SetMaterial(0, DynamicMaterial);
		StaticMeshComponent->SetMaterial(1, DynamicMaterial);
		StaticMeshComponent->SetMaterial(2, DynamicMaterial);
	}
}

void AVehiclePart::NotifyActorBeginCursorOver()
{
	Super::NotifyActorBeginCursorOver();

	UE_LOG(LogTemp, Warning, TEXT("BEGIN MOUSE OVER"));
}

void AVehiclePart::NotifyActorEndCursorOver()
{
	Super::NotifyActorEndCursorOver();

	UE_LOG(LogTemp, Warning, TEXT("END MOUSE OVER"));
}

void AVehiclePart::NotifyActorOnClicked()
{
	Super::NotifyActorOnClicked();

	static bool foo = false;

	UE_LOG(LogTemp, Warning, TEXT("CLICK!"));

	if (DynamicMaterial)
		DynamicMaterial->SetScalarParameterValue(FName(TEXT("IsSelected")), foo);

	foo = !foo;
}

// Called when the game starts or when spawned
void AVehiclePart::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void AVehiclePart::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	/*for(int32 b = 0; b < Children.Num(); b++)
	{
	UE_LOG(LogTemp, Warning, TEXT("%s"), *(Children**->GetActorLabel()));
	}*/
}

int32 AVehiclePart::GetEnergyContribution()
{
	return 0;
}

int32 AVehiclePart::GetHealthRegeneration()
{
	return 0;
}

I get the log message for the click but the material isn’t changing.

in OnConstruction, you only setup the material if it has already been created, which seems wrong to me. Change to !DynamicMaterial.

I don’t know why you set material 0,1 and 2 to the dynamic material. Shouldn’t that just be 0?

In the click function you would first change the foo value and then call setScalarParameterValue, or else it won’t work the first click.
Then alpha is generally not a bool, but a float. I think it does go from 0 to 1, but might also be 0 to 100, not 100% sure.

if I were you I would use a debugger to see what it exactly does.

Ah I meant to write…


if (Material)
	{
		DynamicMaterial = UMaterialInstanceDynamic::Create(Material, this);
		StaticMeshComponent->SetMaterial(0, DynamicMaterial);
		StaticMeshComponent->SetMaterial(1, DynamicMaterial);
		StaticMeshComponent->SetMaterial(2, DynamicMaterial);
	}

Let me try that!

I edited out the checks, and ran this code:


#include "Editor3.h"
#include "VehiclePart.h"

AVehiclePart::AVehiclePart()
{
	// Set this actor to call Tick() every frame
	PrimaryActorTick.bCanEverTick = true;

	// Create and attach the scene component to root
	RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root Component"));

	// Create and attach the static mesh component to our scene component
	StaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Static Mesh Component"));
	StaticMeshComponent->AttachTo(RootComponent);

	static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("Material'/Game/Materials/FancyPlastic.FancyPlastic'"));

	if (MaterialAsset.Succeeded()) Material = (UMaterial *) MaterialAsset.Object;

}

void AVehiclePart::OnConstruction(const FTransform & Transform)
{
	Super::OnConstruction(Transform);

	// Setup the dynamic material
	DynamicMaterial = UMaterialInstanceDynamic::Create(Material, this);

	StaticMeshComponent->SetMaterial(0, DynamicMaterial);
	StaticMeshComponent->SetMaterial(1, DynamicMaterial);
	StaticMeshComponent->SetMaterial(2, DynamicMaterial);
}

void AVehiclePart::NotifyActorBeginCursorOver()
{
	Super::NotifyActorBeginCursorOver();

	UE_LOG(LogTemp, Warning, TEXT("BEGIN MOUSE OVER"));
}

void AVehiclePart::NotifyActorEndCursorOver()
{
	Super::NotifyActorEndCursorOver();

	UE_LOG(LogTemp, Warning, TEXT("END MOUSE OVER"));
}

void AVehiclePart::NotifyActorOnClicked()
{
	Super::NotifyActorOnClicked();

	static bool foo = false;

	UE_LOG(LogTemp, Warning, TEXT("CLICK!"));

	DynamicMaterial->SetScalarParameterValue(FName(TEXT("IsSelected")), foo);

	foo = !foo;
}

// Called when the game starts or when spawned
void AVehiclePart::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void AVehiclePart::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	/*for(int32 b = 0; b < Children.Num(); b++)
	{
	UE_LOG(LogTemp, Warning, TEXT("%s"), *(Children**->GetActorLabel()));
	}*/
}

int32 AVehiclePart::GetEnergyContribution()
{
	return 0;
}

int32 AVehiclePart::GetHealthRegeneration()
{
	return 0;
}

It crashed the editor when I clicked on the Actor, so I ran the code with a breakpoint on the following line:


DynamicMaterial->SetScalarParameterValue(FName(TEXT("IsSelected")), foo);

And when the breakpoint activated, I notice that DynamicMaterial is null. Not sure why as I set it in the OnConstruction method.

Success!

Totally forgot the UPROPERTY macros in the header, the Materials were getting obliterated by garbage collection before I had a chance to click on them.