Unreal Engine 5 Gameplay Framework

in unreal engine 5

Introduction to Gameplay Framework

Overview of the Gameplay Framework's Role and Importance

Unreal Engine 5’s gameplay framework is the backbone of game logic and mechanics, providing a structured approach to developing games. It offers a set of classes and components that define how a game operates, from player interactions to game rules. Understanding this framework is crucial for anyone looking to create sophisticated and responsive games.

The gameplay framework in Unreal Engine 5 is designed to be flexible and modular, allowing developers to customize and extend it according to their game’s needs. By leveraging this framework, you can efficiently manage game states, player interactions, AI behaviors, and more.

Basic Architecture and Key Components

The basic architecture of Unreal Engine 5’s gameplay framework consists of several key components, each serving a specific purpose:

  1. GameMode:

    • Defines the rules and flow of the game.
    • Manages the game’s state and controls the transition between different states (e.g., starting, playing, ending).
  2. GameState:

    • Holds the game’s state and replicates it across clients in a multiplayer game.
    • Stores information about the game’s progress, such as the score or remaining time.
  3. PlayerController:

    • Acts as the player’s interface to the game.
    • Handles player input and controls the player’s Pawn or Character.
  4. Pawn and Character:

    • Represent the controllable entities in the game.
    • Character is a specialized type of Pawn with built-in movement and collision handling.
  5. PlayerState:

    • Stores information specific to each player, such as their score or health.
    • Useful in multiplayer games to keep track of individual player data.
  6. HUD (Heads-Up Display):

    • Manages the user interface displayed on the screen.
    • Displays important game information like health, score, and inventory.
  7. Controller (AIController vs. PlayerController):

    • AIController handles the behavior of non-player characters (NPCs).
    • PlayerController manages the player’s actions and input.
  8. Level Blueprint:

    • Contains scripting specific to a particular level.
    • Useful for managing level-specific events and interactions.
  9. Actor and Components:

    • Actor is the base class for all objects that can be placed in a level.
    • Components are used to add functionality to Actors, such as rendering, physics, and audio.

Why Understanding the Gameplay Framework is Crucial

Understanding Unreal Engine 5’s gameplay framework is essential because it allows you to:

  • Create Complex Game Logic: By using the framework’s components, you can build intricate game mechanics and interactions.
  • Enhance Multiplayer Experiences: The framework’s classes are designed to support multiplayer functionality, making it easier to develop online games.
  • Improve Game Performance: Properly utilizing the framework can help optimize your game’s performance, ensuring smooth gameplay.
  • Facilitate Team Collaboration: A clear understanding of the framework promotes better collaboration among team members, as everyone can work within the same structured environment.

In the next sections, I will delve deeper into each of these key components, starting with GameMode, to help you master Unreal Engine 5’s gameplay framework and take full advantage of its capabilities.

in unreal engine 5

Understanding GameMode

What is GameMode and Its Purpose

The GameMode class in Unreal Engine 5 is a critical part of the gameplay framework. It defines the rules and flow of the game, such as which pawn the player controls, what happens when players die, and how the game transitions between different states. Essentially, GameMode serves as the director of your game, orchestrating the various elements to ensure a cohesive and enjoyable experience.

A few key responsibilities of GameMode include:

  • Determining the default pawn class that players will control.
  • Specifying the HUD class used to display information on the screen.
  • Setting the player controller class that will handle input from players.
  • Managing game state transitions, such as starting, pausing, and ending the game.

Creating and Customizing a GameMode

Creating and customizing a GameMode in Unreal Engine 5 is straightforward. Here’s how you can create a custom GameMode and tailor it to fit your game’s needs:

  1. Creating a Custom GameMode:

    • Open your Unreal Engine project.
    • In the Content Browser, right-click and select Blueprint Class.
    • Choose GameModeBase as the parent class. Name your new GameMode (e.g., MyCustomGameMode).
  2. Customizing GameMode Properties:

    • Open your newly created GameMode Blueprint.
    • In the Details panel, you can set various properties such as Default Pawn Class, HUD Class, Player Controller Class, and more.
    • For example, set the Default Pawn Class to a custom pawn you have created (e.g., MyCustomPawn).
  3. Setting the GameMode for Your Level:

    • Open the level where you want to use the custom GameMode.
    • Go to World Settings (Window > World Settings).
    • Under GameMode Override, select your custom GameMode (e.g., MyCustomGameMode).

Differences Between GameMode and GameModeBase

In Unreal Engine, there are two primary classes related to GameMode: GameMode and GameModeBase. Understanding the differences between these classes will help you choose the right one for your needs.

  1. GameModeBase:

    • A simpler version of GameMode.
    • Provides basic functionality for defining game rules and flow.
    • Suitable for scenarios where you need minimal game logic and customization.
  2. GameMode:

    • Derived from GameModeBase.
    • Includes additional features and logic, such as handling player respawns and game-specific rules.
    • Ideal for more complex games where you need extensive control over game mechanics.

In general, if your game requires minimal logic and rules, you can start with GameModeBase. If you need more control and additional functionality, GameMode is the better choice.

Example: Customizing GameMode

Let’s walk through an example of creating and customizing a GameMode to demonstrate its capabilities.

  1. Creating a Custom Pawn:

    • Right-click in the Content Browser and select Blueprint Class.
    • Choose Pawn as the parent class. Name it (e.g., MyCustomPawn).
    • Open the pawn and add components like a static mesh or camera to define its appearance and behavior.
  2. Creating a Custom GameMode:

    • Right-click in the Content Browser and select Blueprint Class.
    • Choose GameModeBase as the parent class. Name it (e.g., MyCustomGameMode).
    • Open the GameMode Blueprint and set the Default Pawn Class to your custom pawn (MyCustomPawn).
  3. Setting Up the Level:

    • Open the level where you want to use the custom GameMode.
    • Go to World Settings and set the GameMode Override to your custom GameMode (MyCustomGameMode).
  4. Adding Custom Logic:

    • Open your custom GameMode Blueprint.
    • Add logic to handle game-specific events, such as starting the game, handling player deaths, or updating the score.

By following these steps, you can create a customized GameMode that fits the unique needs of your game. This allows you to define the rules and flow of your game, ensuring a cohesive and engaging player experience.

In the next section, I will explore GameState and PlayerState, which are crucial for managing game and player-specific data.

in unreal engine 5

Exploring GameState and PlayerState

Purpose of GameState and PlayerState

In Unreal Engine 5, GameState and PlayerState are essential components for managing and tracking the state of the game and individual players. These classes are particularly important in multiplayer games where you need to keep track of the game’s overall state and each player’s specific data.

GameState is responsible for maintaining the state of the game that needs to be known and replicated across all clients. This includes information like the current score, remaining time, and the overall game phase.

PlayerState, on the other hand, is used to keep track of information specific to each player, such as their score, health, or other statistics. This class is also replicated, meaning each client will have up-to-date information about every player in the game.

How GameState and PlayerState Interact with Other Components

GameState and PlayerState work closely with other components of the gameplay framework to ensure that both the game and player states are accurately managed and synchronized.

  • GameMode: GameMode is responsible for spawning and initializing GameState and PlayerState objects. It dictates the rules of the game and interacts with these classes to enforce those rules.
  • PlayerController: PlayerController communicates with PlayerState to manage player-specific data and interactions.
  • HUD: The HUD uses information from PlayerState to display relevant data to the player, such as health, score, or inventory.

Customizing GameState and PlayerState for Your Game

To effectively use GameState and PlayerState in your game, you may need to create custom subclasses that extend their functionality to meet your specific needs.

Creating a Custom GameState:

1.Create a Custom GameState Class:

    • In the Content Browser, right-click and select Blueprint Class.
    • Choose GameStateBase as the parent class and name it (e.g., MyCustomGameState).

2.Add Custom Properties and Logic:

    • Open your custom GameState Blueprint.
    • Add variables to store game-specific data, such as current score or remaining time.
    • Implement custom logic to update these variables as needed.

Example: Custom GameState Implementation:

				
					// MyCustomGameState.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "MyCustomGameState.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomGameState : public AGameStateBase
{
    GENERATED_BODY()

public:
    AMyCustomGameState();

    UPROPERTY(BlueprintReadWrite, Replicated, Category = "Game")
    int32 CurrentScore;

    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};

// MyCustomGameState.cpp
#include "MyCustomGameState.h"
#include "Net/UnrealNetwork.h"

AMyCustomGameState::AMyCustomGameState()
{
	CurrentScore = 0;
}

void AMyCustomGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(AMyCustomGameState, CurrentScore);
}
				
			

Creating a Custom PlayerState:

1.Create a Custom PlayerState Class:

    • In the Content Browser, right-click and select Blueprint Class.
    • Choose PlayerState as the parent class and name it (e.g., MyCustomPlayerState).

2.Add Custom Properties and Logic:

    • Open your custom PlayerState Blueprint.
    • Add variables to store player-specific data, such as health or score.
    • Implement custom logic to update these variables as needed.

Example: Custom PlayerState Implementation:

				
					// MyCustomPlayerState.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "MyCustomPlayerState.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomPlayerState : public APlayerState
{
	GENERATED_BODY()

public:
	AMyCustomPlayerState();

	UPROPERTY(BlueprintReadWrite, Replicated, Category = "Player")
	int32 PlayerScore;

	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};

// MyCustomPlayerState.cpp
#include "MyCustomPlayerState.h"
#include "Net/UnrealNetwork.h"

AMyCustomPlayerState::AMyCustomPlayerState()
{
	PlayerScore = 0;
}

void AMyCustomPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(AMyCustomPlayerState, PlayerScore);
}
				
			

Implementing and Using Custom GameState and PlayerState

  1. Set the Custom Classes in GameMode:

    • Open your custom GameMode Blueprint (e.g., MyCustomGameMode).
    • In the Details panel, set the GameState Class to your custom GameState (MyCustomGameState) and the PlayerState Class to your custom PlayerState (MyCustomPlayerState).
  2. Use the Custom Classes in Your Game:

    • Now, your game will use the custom GameState and PlayerState classes. You can access and modify their properties through your game logic.

By customizing GameState and PlayerState, you can effectively manage and track both game-wide and player-specific data, ensuring a smooth and synchronized multiplayer experience.

In the next section, I will dive into the PlayerController, which acts as the player’s interface to the game.

in unreal engine 5

PlayerController: The Player's Interface

Role of PlayerController in Handling Player Input

The PlayerController class in Unreal Engine 5 acts as the main interface between the player and the game. It is responsible for processing player input, such as keyboard and mouse actions, and translating those inputs into actions within the game world. PlayerController also manages the player’s camera and can handle UI interactions.

Key responsibilities of the PlayerController include:

  • Receiving and processing input from the player.
  • Controlling the player’s Pawn or Character, dictating how it moves and interacts with the environment.
  • Managing the player’s camera, ensuring that it follows the player’s movements and provides the appropriate perspective.
  • Handling user interface interactions, such as responding to menu selections or HUD elements.

Creating and Customizing PlayerController

To create a custom PlayerController and tailor it to your game’s needs, follow these steps:

  1. Creating a Custom PlayerController:

    • Open the Content Browser, right-click, and select Blueprint Class.
    • Choose PlayerController as the parent class. Name it (e.g., MyCustomPlayerController).
  2. Customizing PlayerController Properties:

    • Open your newly created PlayerController Blueprint.
    • In the Event Graph, you can add custom logic to handle player input and other interactions.
  3. Setting the PlayerController in GameMode:

    • Open your custom GameMode Blueprint (e.g., MyCustomGameMode).
    • In the Details panel, set the Player Controller Class to your custom PlayerController (MyCustomPlayerController).

Example: Customizing PlayerController

Let’s walk through an example of creating and customizing a PlayerController to handle custom input and control a player’s Pawn.

  1. Creating a Custom Pawn:

    • Right-click in the Content Browser and select Blueprint Class.
    • Choose Pawn as the parent class. Name it (e.g., MyCustomPawn).
    • Open the pawn and add components like a static mesh or camera to define its appearance and behavior.
  2. Creating a Custom PlayerController:

    • Right-click in the Content Browser and select Blueprint Class.
    • Choose PlayerController as the parent class. Name it (e.g., MyCustomPlayerController).
  3. Handling Input in PlayerController:

    • Open your custom PlayerController Blueprint.
    • In the Event Graph, add nodes to handle input events. For example, to handle forward movement:
				
					// MyCustomPlayerController.cpp
#include "MyCustomPlayerController.h"
#include "GameFramework/Actor.h"

void AMyCustomPlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();

    // Bind input functions
    InputComponent->BindAxis("MoveForward", this, &AMyCustomPlayerController::MoveForward);
}

void AMyCustomPlayerController::MoveForward(float Value)
{
    if (Value != 0.0f)
    {
        // Get the pawn controlled by this controller
        APawn* ControlledPawn = GetPawn();
        if (ControlledPawn)
        {
            // Move the pawn forward
            ControlledPawn->AddMovementInput(FVector::ForwardVector, Value);
        }
    }
}

				
			

4.Setting Up the Level:

  • Open the level where you want to use the custom PlayerController.
  • Go to World Settings and set the GameMode Override to your custom GameMode (MyCustomGameMode).

5.Testing the Custom PlayerController:

  • Play the level and test the custom PlayerController to ensure it handles input and controls the pawn as expected.

Managing Multiple Players with PlayerControllers

In multiplayer games, each player will have their own PlayerController instance. Unreal Engine manages these PlayerControllers to ensure that input from different players is correctly processed and applied to their respective Pawns.

  • Local Multiplayer: In a local multiplayer setup (e.g., split-screen), multiple PlayerControllers are created on the same client, each controlling a different Pawn.
  • Networked Multiplayer: In a networked multiplayer setup, each client has its own PlayerController, and the server manages the synchronization of these controllers and their Pawns.

To support multiple players, ensure that your game logic correctly handles the input and actions from each PlayerController without conflicts.

In the next section, I will explore Pawns and Characters, which are the controllable entities that players interact with in the game.

in unreal engine 5

Pawn and Character: Controllable Entities

Differences Between Pawn and Character

In Unreal Engine 5, Pawn and Character are two fundamental classes that represent controllable entities within the game. While both classes are used to create entities that players or AI can control, they serve different purposes and come with distinct features.

  1. Pawn:

    • A generic actor that can be possessed and controlled by a PlayerController or AIController.
    • Does not include built-in movement capabilities or animations.
    • Ideal for simple entities, such as vehicles, cameras, or basic AI.
  2. Character:

    • A specialized type of Pawn with built-in movement and animation capabilities.
    • Comes with a CharacterMovementComponent that provides walking, running, jumping, and other movement functionalities.
    • Suitable for humanoid or complex characters that require animations and advanced movement mechanics.

Creating and Customizing Pawns and Characters

To create and customize Pawns and Characters in Unreal Engine 5, follow these steps:

Creating a Custom Pawn:

  1. Create a Custom Pawn Class:

    • In the Content Browser, right-click and select Blueprint Class.
    • Choose Pawn as the parent class and name it (e.g., MyCustomPawn).
  2. Add Components and Logic:

    • Open your custom Pawn Blueprint.
    • Add components such as a Static Mesh, Camera, or Collision component to define its appearance and behavior.
    • In the Event Graph, add logic to handle input and interactions.

Example: Custom Pawn Implementation:

				
					// MyCustomPawn.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyCustomPawn.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomPawn : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	AMyCustomPawn();

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

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	// Custom movement function
	void MoveForward(float Value);
};

// MyCustomPawn.cpp
#include "MyCustomPawn.h"


// Sets default values
AMyCustomPawn::AMyCustomPawn()
{
	PrimaryActorTick.bCanEverTick = true;
}

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

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

// Called to bind functionality to input
void AMyCustomPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	PlayerInputComponent->BindAxis("MoveForward", this, &AMyCustomPawn::MoveForward);
}

void AMyCustomPawn::MoveForward(float Value)
{
	if (Value != 0.0f)
	{
		AddMovementInput(FVector::ForwardVector, Value);
	}
}
				
			

Creating a Custom Character:

1.Create a Custom Character Class:

  • In the Content Browser, right-click and select Blueprint Class.
  • Choose Character as the parent class and name it (e.g., MyCustomCharacter).

2.Add Components and Logic:

  • Open your custom Character Blueprint.
  • Add components such as a Skeletal Mesh, Camera, or Collision component to define its appearance and behavior.
  • Use the CharacterMovementComponent to set up movement properties like walk speed, jump height, etc.
    • In the Event Graph, add logic to handle input and interactions.

Example: Custom Character Implementation:

				
					// MyCustomCharacter.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyCustomCharacter.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMyCustomCharacter();

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

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	// Custom movement function
	void MoveForward(float Value);

	UFUNCTION(BlueprintCallable)
	float GetHealth();

	UFUNCTION(BlueprintCallable)
	float GetMaxHealth();
	
};

// MyCustomCharacter.cpp
#include "MyCustomCharacter.h"

// Sets default values
AMyCustomCharacter::AMyCustomCharacter()
{
	PrimaryActorTick.bCanEverTick = true;
}

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

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

// Called to bind functionality to input
void AMyCustomCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	PlayerInputComponent->BindAxis("MoveForward", this, &AMyCustomCharacter::MoveForward);
}

void AMyCustomCharacter::MoveForward(float Value)
{
	if (Value != 0.0f)
	{
		AddMovementInput(FVector::ForwardVector, Value);
	}
}

float AMyCustomCharacter::GetHealth()
{
	return 90;
}

float AMyCustomCharacter::GetMaxHealth()
{
	return 100;
}
				
			

Using Input Bindings and Components with Pawns and Characters

Both Pawns and Characters can be customized to handle specific player inputs and use various components to achieve desired behaviors. Input bindings can be set up to map player actions (e.g., moving, jumping) to specific functions within your Pawn or Character classes.

  1. Setting Up Input Bindings:

    • Open the Project Settings and navigate to Input.
    • Add Axis Mappings and Action Mappings for the controls you need (e.g., MoveForward, Jump).
  2. Using Components:

    • Add components like Static Mesh, Skeletal Mesh, Camera, and others to your Pawn or Character to define their appearance and behavior.
    • Configure these components in the Blueprint Editor or via code to achieve the desired functionality.

By creating and customizing Pawns and Characters, you can define the controllable entities within your game, handling input and interactions to create a responsive and engaging player experience.

In the next section, I will explore the HUD and User Interface, which are crucial for displaying game information to the player.

in unreal engine 5

HUD and User Interface

Understanding the HUD Class and Its Purpose

The HUD (Heads-Up Display) class in Unreal Engine 5 is responsible for managing and displaying the user interface elements that provide critical information to the player during gameplay. This can include health bars, scores, ammo counts, minimaps, and more. The HUD class acts as a canvas on which you can draw these elements, ensuring that the player always has access to essential game data.

Creating and Managing User Interfaces with UMG

Unreal Motion Graphics (UMG) is Unreal Engine’s built-in tool for creating complex and visually appealing user interfaces. UMG allows you to design UI elements using a visual editor and bind them to game logic through Blueprints or C++.

Creating a Simple HUD with UMG:

1.Create a UMG Widget:

  • Open the Content Browser, right-click, and select User Interface > Widget Blueprint.
  • Name your new widget (e.g., MyHUDWidget).

2.Design the HUD:

  • Open your Widget Blueprint.
  • Use the visual designer to add UI elements like Text Blocks, Progress Bars, and Buttons.
  • Arrange and style these elements to create your desired HUD layout.

3.Implement HUD Logic:

  • In the Event Graph of your Widget Blueprint, add logic to update the UI elements based on game data. For example, bind the value of a Progress Bar to the player’s health.

4.Create a Custom HUD Class:

  • In the Content Browser, right-click and select Blueprint Class.
  • Choose HUD as the parent class and name it (e.g., MyCustomHUD).

5.Display the Widget on the HUD:

  • Open your custom HUD Blueprint.
  • In the Event Graph, create logic to add the widget to the viewport when the game starts.

Example: Custom HUD Implementation:

NOTE: Before using AHUD, need to add “UMG” to your dependency in Build.cs

				
					// MyCustomHUD.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "MyCustomHUD.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomHUD : public AHUD
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;

public:
	UPROPERTY(EditAnywhere, Category = "HUD")
	TSubclassOf<class UUserWidget> HUDWidgetClass;

private:
	UUserWidget* CurrentWidget;
};

// MyCustomHUD.cpp
#include "MyCustomHUD.h"
#include "Blueprint/UserWidget.h"

void AMyCustomHUD::BeginPlay()
{
	Super::BeginPlay();

	if (HUDWidgetClass != nullptr)
	{
		CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), HUDWidgetClass);
		if (CurrentWidget != nullptr)
		{
			CurrentWidget->AddToViewport();
		}
	}
}
				
			

6.Set the Custom HUD in GameMode:

  • Open your custom GameMode Blueprint (e.g., MyCustomGameMode).
  • In the Details panel, set the HUD Class to your custom HUD (MyCustomHUD).

Customizing the HUD to Display Game-Specific Information

The power of UMG and the HUD class lies in their flexibility. You can customize the HUD to display any game-specific information you need, such as:

  • Health and Stamina Bars: Use Progress Bars to show the player’s health and stamina levels.
  • Score and Ammo Counts: Display Text Blocks to show the player’s current score and remaining ammo.
  • Minimap: Integrate a minimap to help players navigate the game world.
  • Inventory: Create an inventory system that displays the items the player is carrying.

Example: Updating the Health Bar:

1.Add a Health Bar to the Widget:

  • In your Widget Blueprint (e.g., MyHUDWidget), add a Progress Bar and name it (e.g., HealthBar).

2.Bind the Health Bar to Player Health:

  • In the Event Graph, create a binding for the Health Bar’s Percent property.
  • Write logic to update this binding based on the player’s health.
				
					// MyHUDWidget.h
#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "MyHUDWidget.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API UMyHUDWidget : public UUserWidget
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintCallable, Category = "HUD")
	float GetHealthPercent() const;
};

// MyHUDWidget.cpp
#include "MyHUDWidget.h"
#include "GameplayFramework/Public/MyCustomCharacter.h"

float UMyHUDWidget::GetHealthPercent() const
{
	AMyCustomCharacter* PlayerCharacter = Cast<AMyCustomCharacter>(GetOwningPlayerPawn());
	if (PlayerCharacter)
	{
		return PlayerCharacter->GetHealth() / PlayerCharacter->GetMaxHealth();
	}
	return 0.0f;
}
				
			

In the next section, I will explore the role of Controllers, differentiating between AIController and PlayerController, and explaining how to implement AI behavior in your game.

in unreal engine 5

The Role of Controllers: AIController vs. PlayerController

Differences Between AIController and PlayerController

In Unreal Engine 5, Controllers are responsible for directing the behavior of Pawns and Characters. There are two main types of controllers: PlayerController and AIController.

  1. PlayerController:

    • Manages input from the player.
    • Translates player input into actions within the game world.
    • Controls the player’s Pawn or Character, handling movements, interactions, and camera control.
  2. AIController:

    • Manages the behavior of non-player characters (NPCs).
    • Uses artificial intelligence to dictate the actions of the NPC.
    • Can be programmed to follow specific behaviors, make decisions, and respond to the game environment.

Implementing AI Behavior with AIController

AIControllers are used to implement AI behavior in Unreal Engine 5. By creating custom AIControllers, you can define complex behaviors for your NPCs, making your game world more dynamic and engaging.

Creating a Custom AIController:

1.Create a Custom AIController Class:

  • In the Content Browser, right-click and select Blueprint Class.
  • Choose AIController as the parent class and name it (e.g., MyCustomAIController).

2.Add AI Logic:

  • Open your custom AIController Blueprint.
  • In the Event Graph, add nodes to define the AI’s behavior. You can use behavior trees, blackboards, and custom logic to control the AI.

Example: Custom AIController Implementation:

NOTE: Before using AHUD, need to add “AIModule” and “NavigationSystem” to your dependency in ProjectName.Build.cs

				
					// MyCustomAIController.h
#pragma once

#include "CoreMinimal.h"
#include "AIController.h"

#include "MyCustomAIController.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomAIController : public AAIController
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;

private:
	void MoveToRandomLocation();
};

// MyCustomAIController.cpp
#include "MyCustomAIController.h"
#include "NavigationSystem.h"

void AMyCustomAIController::BeginPlay()
{
	Super::BeginPlay();

	MoveToRandomLocation();
}

void AMyCustomAIController::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	// Add any additional AI logic here
}

void AMyCustomAIController::MoveToRandomLocation()
{
	UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetCurrent(GetWorld());
	if (NavSystem)
	{
		FNavLocation RandomLocation;
		if (NavSystem->GetRandomPointInNavigableRadius(GetPawn()->GetActorLocation(), 1000.0f, RandomLocation))
		{
			MoveToLocation(RandomLocation);
		}
	}
}
				
			

3.Assign the AIController to an NPC:

  • Create a new Pawn or Character Blueprint for your NPC.
  • In the Details panel, set the AI Controller Class to your custom AIController (MyCustomAIController).

4.Using Behavior Trees and Blackboards:

  • Unreal Engine 5 offers powerful tools for AI development through behavior trees and blackboards. Behavior trees allow you to create complex decision-making structures, while blackboards store data that the AI can use to make decisions.
  • To create a behavior tree, right-click in the Content Browser, select Artificial Intelligence > Behavior Tree, and name it (e.g., MyBehaviorTree).
  • Create a blackboard by right-clicking in the Content Browser, selecting Artificial Intelligence > Blackboard, and naming it (e.g., MyBlackboard).
  • Open the behavior tree and set up tasks, decorators, and services to define your AI’s behavior. Use the blackboard to store and retrieve data.

Example: Setting Up a Simple Patrol Behavior with Behavior Trees

1.Create Behavior Tree and Blackboard:

  • Right-click in the Content Browser and create a new Behavior Tree (e.g., MyPatrolBehaviorTree) and Blackboard (e.g., MyPatrolBlackboard).

2.Define Blackboard Keys:

  • Open the blackboard and add keys for variables you want to track, such as patrol points.

3.Set Up Behavior Tree:

  • Open the behavior tree and create a sequence of tasks for patrolling.
  • Add tasks like moving to patrol points and waiting at each point.

4.Implement AI Logic:

  • In the AIController, add logic to use the behavior tree and blackboard.

Example: Setting Up the Behavior Tree in AIController:

				
					// MyCustomAIController.h
#pragma once

#include "CoreMinimal.h"
#include "AIController.h"

#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BlackboardComponent.h"

#include "MyCustomAIController.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomAIController : public AAIController
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;

private:
	void MoveToRandomLocation();
	
	UPROPERTY(EditAnywhere)
	UBehaviorTree* BehaviorTree;

	UPROPERTY(EditAnywhere)
	UBlackboardData* BlackboardData;

	UPROPERTY(EditAnywhere)
	UBlackboardComponent* BlackboardComponent;
};

// MyCustomAIController.cpp
#include "MyCustomAIController.h"
#include "NavigationSystem.h"

void AMyCustomAIController::BeginPlay()
{
	Super::BeginPlay();

	if (BehaviorTree)
	{
		UseBlackboard(BlackboardData, BlackboardComponent);
		RunBehaviorTree(BehaviorTree);
	}
	
	MoveToRandomLocation();
}

void AMyCustomAIController::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	// Add any additional AI logic here
}

void AMyCustomAIController::MoveToRandomLocation()
{
	UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetCurrent(GetWorld());
	if (NavSystem)
	{
		FNavLocation RandomLocation;
		if (NavSystem->GetRandomPointInNavigableRadius(GetPawn()->GetActorLocation(), 1000.0f, RandomLocation))
		{
			MoveToLocation(RandomLocation);
		}
	}
}
				
			

5.Assign the Behavior Tree to the AIController:

  • In your custom AIController Blueprint, assign the behavior tree and blackboard in the Details panel.

By following these steps, you can create sophisticated AI behaviors using AIController, behavior trees, and blackboards, making your game world more immersive and interactive.

In the next section, I will explore the Level Blueprint, which is used for scripting specific to a particular level.

in unreal engine 5

Using the Level Blueprint

Introduction to the Level Blueprint and Its Uses

The Level Blueprint in Unreal Engine 5 is a specialized type of Blueprint that is tied to a specific level. It provides a convenient way to script and manage events, interactions, and behaviors that are unique to that level. The Level Blueprint is particularly useful for managing level-specific logic, such as triggering events when a player enters an area, controlling environmental effects, or coordinating sequences of actions.

Common Scenarios Where Level Blueprints Are Useful

Level Blueprints are ideal for handling various scenarios that are specific to a particular level, including:

  1. Triggering Events:

    • Setting up triggers to start events when players enter certain areas.
    • Managing timed events or sequences, such as opening doors or starting cutscenes.
  2. Controlling Environmental Effects:

    • Adjusting lighting, weather, or other environmental effects dynamically.
    • Managing sound effects and music transitions.
  3. Coordinating Actions:

    • Orchestrating complex sequences, such as a series of explosions or a timed puzzle.
    • Managing level-specific gameplay mechanics, like elevators or moving platforms.
  4. Handling Level-Specific Interactions:

    • Implementing custom interactions that are unique to the level, such as unlocking a secret area or collecting a special item.

Creating and Using a Level Blueprint

To create and use a Level Blueprint, follow these steps:

1.Open the Level Blueprint:

  • Open your level in the Unreal Engine Editor.
  • In the toolbar, click on Blueprints and select Open Level Blueprint.

2.Adding Logic to the Level Blueprint:

  • The Level Blueprint editor is similar to the Blueprint editor for other classes. You can add nodes to create logic that will be executed within the level.
  • Use the Event Graph to set up event nodes, such as Event BeginPlay or custom trigger events.

Example: Triggering an Event When the Player Enters a Trigger Volume:

1.Place a Trigger Volume in the Level:

  • In the Content Browser, go to Modes and select Volumes.
  • Drag and drop a Trigger Volume into the level.

2.Set Up the Trigger Event in the Level Blueprint:

  • Open the Level Blueprint.
  • Right-click in the Event Graph and add an OnActorBeginOverlap event for the Trigger Volume.
  • Connect the event to the desired actions, such as playing a sound or starting a sequence.
				
					// LevelBlueprint (Event Graph)
Event TriggerVolume.OnActorBeginOverlap
  -> Play Sound at Location
  -> Start Custom Sequence
				
			

3.Adding Custom Logic:

  • You can add custom logic to the Level Blueprint to handle various interactions and events.
  • Use Blueprint nodes to create complex sequences, manage variables, and interact with other actors in the level.

Example: Managing Environmental Effects

Adjusting Lighting Based on Time of Day:

1.Create a Directional Light in the Level:

  • In the Content Browser, go to Modes and select Lights.
  • Drag and drop a Directional Light into the level.

2.Set Up a Timeline in the Level Blueprint:

  • Open the Level Blueprint.
  • Right-click in the Event Graph and add an Event Tick node.
  • Create a Timeline node to animate the lighting over time.
				
					// LevelBlueprint (Event Graph)
Event Tick
  -> Timeline (DayNightCycle)
      -> Set Directional Light Rotation
				
			

3.Configure the Timeline:

  • Double-click the Timeline node to open the Timeline Editor.
  • Add a float track to animate the rotation of the directional light over time.

By using Level Blueprints, you can effectively manage level-specific logic and create dynamic, interactive environments that enhance the player’s experience.

In the next section, I will explore Actors and Components, which are fundamental building blocks for creating interactive elements in your game.

 

in unreal engine 5

Actors and Components

Understanding the Actor Class and Its Role in the Gameplay Framework

In Unreal Engine 5, the Actor class is the base class for all objects that can be placed or spawned in a level. Actors are the fundamental building blocks of a game, representing everything from characters and vehicles to lights and triggers. The Actor class provides a wide range of functionality, including the ability to move, rotate, and interact with other objects.

Key features of the Actor class include:

  • Transform Properties: Actors have transform properties (location, rotation, and scale) that determine their position and orientation in the world.
  • Components: Actors are composed of components that define their appearance and behavior.
  • Lifecycle Events: Actors have lifecycle events such as BeginPlay, Tick, and EndPlay, which allow you to implement custom logic at different stages of the Actor’s lifecycle.

Creating and Managing Components within Actors

Components are modular pieces of functionality that can be added to Actors to define their behavior and appearance. Common types of components include Static Mesh Components, Skeletal Mesh Components, Collision Components, and Audio Components.

Creating a Custom Actor:

1.Create a Custom Actor Class:

  • In the Content Browser, right-click and select Blueprint Class.
  • Choose Actor as the parent class and name it (e.g., MyCustomActor).

2.Add Components to the Actor:

  • Open your custom Actor Blueprint.
  • In the Components panel, add components to define the Actor’s appearance and behavior. For example, you can add a Static Mesh Component to give the Actor a visual representation.

Example: Custom Actor with Components:

				
					// MyCustomActor.h
#pragma once

#include "CoreMinimal.h"
#include "Components/PointLightComponent.h"
#include "GameFramework/Actor.h"
#include "MyCustomActor.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomActor : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AMyCustomActor();

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

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

private:
	UPROPERTY(EditAnywhere)
	UStaticMeshComponent* MeshComponent;

	UPROPERTY(EditAnywhere)
	UPointLightComponent* LightComponent;
};

// MyCustomActor.cpp
#include "MyCustomActor.h"
#include "Components/StaticMeshComponent.h"
#include "Components/PointLightComponent.h"

// Sets default values
AMyCustomActor::AMyCustomActor()
{
	PrimaryActorTick.bCanEverTick = true;

	// Create and attach components
	MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
	RootComponent = MeshComponent;

	LightComponent = CreateDefaultSubobject<UPointLightComponent>(TEXT("LightComponent"));
	LightComponent->SetupAttachment(RootComponent);
}

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

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

Best Practices for Using Actors and Components

1.Organize Components Hierarchically:

  • Use the component hierarchy to organize components logically. For example, attach a camera component to a mesh component to ensure the camera follows the mesh.

2.Leverage Built-in Components:

  • Take advantage of Unreal Engine’s built-in components, such as Static Mesh Components, Skeletal Mesh Components, and Audio Components, to add functionality to your Actors without needing to write custom code.

3.Implement Reusable Logic:

  • Create reusable components that encapsulate common functionality. This allows you to use the same component across multiple Actors, promoting code reuse and reducing duplication.

4.Optimize Performance:

  • Be mindful of the performance impact of adding many components to an Actor. Use lightweight components when possible and optimize your Actor’s tick functions to minimize performance overhead.

5.Use Actor Lifecycle Events:

  • Implement custom logic in Actor lifecycle events, such as BeginPlay and Tick, to control the Actor’s behavior at different stages. Use these events to initialize components, handle interactions, and perform updates.

In the next section, I will discuss gameplay framework best practices, providing tips for organizing your code and Blueprints, optimizing performance, and avoiding common pitfalls.

in unreal engine 5

Gameplay Framework Best Practices

Tips for Organizing Your Code and Blueprints

Effective organization of your code and Blueprints is crucial for maintaining a clean and manageable project. Here are some best practices to keep your project organized:

  1. Consistent Naming Conventions:

    • Use consistent and descriptive naming conventions for all assets, classes, and Blueprints. For example, prefix classes with their type (e.g., BP_MyCharacter for a Blueprint character).
  2. Folder Structure:

    • Organize your assets into a logical folder structure. Create separate folders for Blueprints, Materials, Textures, Audio, and other asset types. Group related assets together to make them easy to find.
  3. Modular Blueprints:

    • Break down complex Blueprints into smaller, reusable components. This makes them easier to manage and debug. For example, create separate Blueprints for UI elements, player controls, and environmental effects.
  4. Comment and Document:

    • Add comments to your code and Blueprints to explain the logic and functionality. Use the Description field in Blueprints to provide an overview of their purpose and usage.
  5. Use Macros and Functions:

    • Use macros and functions to encapsulate repetitive logic and reduce duplication. This improves readability and makes it easier to update your code.
  6. Version Control:

    • Use a version control system like Git to manage your project’s source code. Commit changes regularly and use branches to work on new features or bug fixes without affecting the main codebase.

Performance Considerations and Optimization Tips

Optimizing your game’s performance is essential for providing a smooth and enjoyable experience. Here are some tips for optimizing performance in Unreal Engine 5:

  1. Profiling Tools:

    • Use Unreal Engine’s built-in profiling tools, such as the Profiler, Stat Commands, and the GPU Visualizer, to identify performance bottlenecks and optimize your game’s performance.
  2. Level of Detail (LOD):

    • Implement LOD for static and skeletal meshes to reduce the complexity of models at a distance. This reduces the rendering load and improves performance.
  3. Occlusion Culling:

    • Use occlusion culling to avoid rendering objects that are not visible to the player. This can significantly reduce the number of draw calls and improve performance.
  4. Efficient Tick Functions:

    • Avoid placing heavy logic in the Tick function, which is called every frame. Use timers or event-driven programming to handle less frequent updates.
  5. Optimized Materials:

    • Optimize your materials by reducing the number of texture samples, using simpler shaders, and combining multiple textures into a single texture atlas.
  6. Streaming Levels:

    • Use level streaming to load and unload parts of your level dynamically. This helps manage memory usage and improves performance, especially in large open-world games.

Common Pitfalls and How to Avoid Them

Here are some common pitfalls in Unreal Engine development and how to avoid them:

1.Overuse of Tick:

  • Avoid over-relying on the Tick function for all updates. Use events, timers, and state machines to manage logic efficiently.

2.Poor Asset Management:

  • Keep your assets organized and avoid duplicating assets unnecessarily. Use references to shared assets instead of creating multiple copies.

3.Ignoring Memory Management:

  • Be mindful of memory usage, especially when spawning actors and using dynamic assets. Implement proper cleanup and memory management to avoid memory leaks.

4.Lack of Testing:

  • Regularly test your game on different hardware and configurations to identify performance issues and bugs. Use automated testing tools to ensure your game runs smoothly across different platforms.

5.Neglecting Multiplayer Considerations:

  • If your game includes multiplayer functionality, ensure that you handle network replication, latency, and synchronization properly. Test multiplayer features extensively to avoid issues.

In the next section, I will explore advanced topics, such as extending the gameplay framework by creating custom gameplay classes and implementing custom game rules and mechanics.

in unreal engine 5

Advanced Topics: Extending the Gameplay Framework

Creating Custom Gameplay Classes

To extend the Unreal Engine 5 gameplay framework, you can create custom gameplay classes that provide additional functionality and tailor the game to your specific needs. Here’s how to create and extend these classes:

  1. Creating a Custom GameMode Class:

    • In the Content Browser, right-click and select Blueprint Class.
    • Choose GameModeBase or GameMode as the parent class and name it (e.g., MyCustomGameMode).
  2. Implement Custom Logic:

    • Open your custom GameMode Blueprint.
    • Add custom rules and logic, such as player spawn rules, game start conditions, and win/lose conditions.

Example: Custom GameMode Implementation:

				
					// MyCustomGameMode.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyCustomGameMode.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomGameMode : public AGameModeBase
{
	GENERATED_BODY()

public:
	AMyCustomGameMode();

protected:
	virtual void BeginPlay() override;

private:
	void StartGame();
	void EndGame();
};

// MyCustomGameMode.cpp
#include "MyCustomGameMode.h"
#include "MyCustomPawn.h"

AMyCustomGameMode::AMyCustomGameMode()
{
	// Set default pawn class to your custom pawn class
	DefaultPawnClass = AMyCustomPawn::StaticClass();
}

void AMyCustomGameMode::BeginPlay()
{
	Super::BeginPlay();
	StartGame();
}

void AMyCustomGameMode::StartGame()
{
	// Custom game start logic
}

void AMyCustomGameMode::EndGame()
{
	// Custom game end logic
}
				
			

Implementing Custom Game Rules and Mechanics

To create unique gameplay experiences, you can implement custom game rules and mechanics in your custom gameplay classes. Here’s an example of adding a scoring system and a custom win condition:

1.Create a Custom GameState Class:

  • In the Content Browser, right-click and select Blueprint Class.
  • Choose GameStateBase as the parent class and name it (e.g., MyCustomGameState).

2.Add Scoring System:

  • Open your custom GameState Blueprint.
  • Add a variable to track the score and a function to update it.

Example: Custom GameState Implementation:

				
					// MyCustomGameState.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "MyCustomGameState.generated.h"

UCLASS()
class GAMEPLAYFRAMEWORK_API AMyCustomGameState : public AGameStateBase
{
    GENERATED_BODY()

public:
    AMyCustomGameState();

    UPROPERTY(BlueprintReadWrite, Replicated, Category = "Game")
    int32 CurrentScore;

    UFUNCTION(BlueprintCallable)
    void AddScore(int32 Points);
    
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};

// MyCustomGameState.cpp
#include "MyCustomGameState.h"
#include "Net/UnrealNetwork.h"

AMyCustomGameState::AMyCustomGameState()
{
	CurrentScore = 0;
}

void AMyCustomGameState::AddScore(int32 Points)
{
	CurrentScore += Points;
	// Check for win condition
	if (CurrentScore >= 100)
	{
		// Custom win condition logic
	}
}

void AMyCustomGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(AMyCustomGameState, CurrentScore);
}
				
			

3.Update GameMode to Use Custom GameState:

  • Open your custom GameMode Blueprint.
  • In the Details panel, set the GameState Class to your custom GameState (MyCustomGameState).

Leveraging Unreal Engine's Modularity for Large-Scale Projects

Unreal Engine 5’s modularity allows you to structure large-scale projects efficiently. Here are some strategies for managing large projects:

1.Modular Game Design:

  • Break down your game into modules, each responsible for a specific aspect of the game (e.g., core gameplay, UI, AI).
  • Use Unreal Engine’s module system to manage dependencies and build each module independently.

2.Reusable Blueprints and Assets:

  • Create reusable Blueprints and assets that can be shared across different levels and projects.
  • Use Blueprint Interfaces to define common functionality that multiple Blueprints can implement.

3.Data-Driven Development:

  • Use data tables and assets to drive game logic. This makes it easier to update game content without modifying code.
  • Separate game logic from game data to simplify balancing and content updates.

4.Automated Testing and Continuous Integration:

  • Implement automated testing to catch bugs and ensure stability.
  • Use continuous integration tools to automate builds and deployments.

Example: Modular Game Structure:

				
					// Module1.Build.cs
using UnrealBuildTool;

public class GameplayFramework : ModuleRules
{
	public GameplayFramework(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "UMG", "AIModule", "NavigationSystem" });

		PrivateDependencyModuleNames.AddRange(new string[] { "Module2" });

		// Uncomment if you are using Slate UI
		// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
		
		// Uncomment if you are using online features
		// PrivateDependencyModuleNames.Add("OnlineSubsystem");

		// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
	}
}


// Module2.Build.cs
using UnrealBuildTool;

public class Module2 : ModuleRules
{
    public Module2(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
    }
}

				
			

Conclusion

Extending the gameplay framework in Unreal Engine 5 allows you to create custom gameplay experiences tailored to your game’s unique needs. By creating custom gameplay classes, implementing custom game rules and mechanics, and leveraging Unreal Engine’s modularity, you can develop sophisticated and scalable game systems.

By following these advanced techniques, you can build robust and flexible gameplay systems that enhance the overall player experience.

With this comprehensive understanding of Unreal Engine 5’s gameplay framework, you are well-equipped to create engaging and dynamic games. Whether you’re managing game states, handling player input, or implementing custom game mechanics, the framework provides the tools and flexibility you need to bring your vision to life.

Your feedback is valuable

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Similar posts

  • Amir Nobandegani
  • Posted by Amir Nobandegani
  • Amir Nobandegani
  • Posted by Amir Nobandegani
  • Amir Nobandegani
  • Posted by Amir Nobandegani

This website stores cookies on your computer. Cookie Policy

Preloader image