Giter Club home page Giter Club logo

ue-fps-rebirth's Introduction

Unreal - ReBirth

English | 简体中文
This is an independently developed game. The purpose is to get started with the virtual 4 game engine, and better compatible with BluePrint and C++ ue4 development. Let us know when to use BluePrint to develop and when to use C++ to develop Unreal 4. This is a very interesting thing. Of course you can: git clone https://github.com/Sugar0612/ReBirth.git or git clone [email protected]:Sugar0612/ReBirth.git to get the source code.

image image

Current process

This is the most recent demo when I was learning to develop with C++/BluePrint. image

Some interesting functions currently involved

Currently, C++ development is the main focus.

  • Regarding the use and declaration of various components (UStaticMeshComponent, UCameraComponent, USphereComponent, UParticleSystemComponent...), they all need

    CreateDefaultSubObject<class* T>(TEXT());

    To create a statement, you also need

    SetupAttachment(GetComponent());

    To rely on the root component, it is worth noting that the collision component generally becomes the root component, namely:

    RootComponent = CollisionComponent;
  • Use and rewrite of Tick function and BeginPlay function, rewrite the general template virtual T function() override;

  • Some mechanisms for character control: inherit APawn / ACharacter to use functions

    SetupPlayerInputComponent(UInputComponent* PlayerInputComponent);
  • Box collision mechanism

    void T::BeginPlay() {
    /* trigger Box Bind Function */
       TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &T::BeginOverlap);
       TriggerBox->OnComponentEndOverlap.AddDynamic(this, &T::EndOverlap);
    }

    Of course you also need to rewrite BeginOverlap and EndOverlap

    UFUNCTION()
    void BeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
    UFUNCTION()
    void EndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
  • HUD window realizes what he added to the game window in my MainPlayerController, and set him in the game mode Player Controller Class.

  • Animation blueprint inherit from UAnimInstance, define a function

    UFUNCTION(BlueprintCallable, Category = Animations)
    void UpdateAnimation();

    Write some action mechanisms in it so that there are judging criteria for switching between each action. Again, use the Event Blueprint Update Animation to call it in the blueprint before.

  • Weapon equipment is inherited from AItem. I asked him to provide the most basic functions for new scene objects, such as particle effects and MeshComponent. We need to add Socket to the character skeleton in advance. This will become the slot for weapon insertion . We will define the corresponding bone control to load the weapon model

    /* *Weapon bone control */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skeletal")
    class USkeletalMeshComponent* SkeletalComponent;

    After creation, we insert the weapon into the slot

    void AWeapon::equipWeapon(AMain* player) {
        /* *Ignore certain types of collisions */
        SkeletalComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);
        SkeletalComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore);
        SkeletalComponent->SetSimulatePhysics(false);
    
        /* *Get a weapon slot */
        const USkeletalMeshSocket* WeaponSocket = player->GetMesh()->GetSocketByName("WeaponSocket");
        if (WeaponSocket) {
    
    /* *If the acquisition is successful, attach it to the slot */
        WeaponSocket->AttachActor(this, player->GetMesh());
        }
    }
  • Montage animation summarizes the basic steps: 1. Create 2. Drag in the animation 3. Segmentation 4. Create slot:'combet' The next step is the setting of AnimInstance_BP. image Then modify the state machine according to different standards. Regarding the definition and use of Montage in C++: Define montage:

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Anims")
    class UAnimMontage* CombatMontage;

    Call the animation of different fragments:

    /* *Get examples of montage */
    UAnimInstance* AnimInstance = this->GetMesh()->GetAnimInstance();
    AnimInstance->Montage_Play(CombatMontage, 1.2f);
    AnimInstance->Montage_JumpToSection(FName("NAME"), CombatMontage);
  • Ai follow/auto attack Use blackboard + behavior tree + AIContorller to realize AI's random finding point and see the player's chasing action. Create the variables you need in the blackboard. Variables are used to make Selector judgments in the behavior tree. You need to change The variables of the blackboard are attached to different Sequence with the attribute Both to implement different Task (you need to go to New Task in the behavior tree). In AIController, you first need to set up the Ai Perception organ Ai Perception For AI Sight Config, then in Blueprint:
    image
    Finally, you only need to set AI Controller Class => AIController in Monster_BP to achieve the action you want. For the triggering of the attack action, I chose to set the box collision in C++ and created the enemy's emun state:

    UENUM(BlueprintType)
    enum class EMonsterState: uint8 {
            EMS_Ldle UMETA(DeplayName = "Ldle"),
            EMS_MoveToTarget UMETA(DeplayName = "MoveToPlayer"),
            EMS_Attacking UMETA(DeplayName = "Attack"),
            EMS_Default UMETA(DeplayName = "Default")
    };

    The collision box changes state:

    if (OtherActor) {
        AMain* Player = Cast<AMain>(OtherActor);
        if (Player) {
           if (MonsterController) {
               bisOverlap = true;
               SetMonsterState(EMonsterState::EMS_Attacking);
           }
        }
    }
  • Particle system, the particle system ParticleSyetem is not the particle control ParticleSystemComponent. The difference between them is that the particle control will always be attached, while the particle system is triggered under a certain mechanism such as collision.... The code to play the particle control system is different: If it¡¯s a weapon model, most of them don¡¯t have StaticMeshComponent but only SkeletalMeshComponent:

    const USkeletalMeshSocket* WeaponSocket = SkeletalComponent->GetSocketByName("WeaponSocket");
    if (WeaponSocket) {
        FVector SocketLocation = WeaponSocket->GetSocketLocation(SkeletalComponent);
        UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), monster->BleedParticles, SocketLocation, FRotator(0.f), false);
    }

    If it is a Monster model, I created a Socket in his hand to make him the place to trigger the particle system, because he comes with StaticMeshComponent:

    const USkeletalMeshSocket* HandSocket = GetMesh()->GetSocketByName("BloodSocket");
    //UE_LOG(LogTemp, Warning, TEXT(" Blood Socket Begin Blood! "));
    if (HandSocket) {
       FVector SocketLocation = HandSocket->GetSocketLocation(GetMesh());
       UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), player->BloodParticles, SocketLocation, FRotator(0.f), false);
    }

    I also need to declare and define the former in the construction:

    SkeletalComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Skeletal Mesh"));
    SkeletalComponent->SetupAttachment(GetRootComponent());
  • Transmission of attack damage. There are many ways to transmit damage from other characters, but ue4 gave us a set of our own ways of transmitting damage:

    /* *Damage mechanism */
    virtual float TakeDamage(float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;

    You need to rewrite the TackDamage function:

    float AMonster::TakeDamage(float DamageTaken, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
    {
        ReduceHp(DamageTaken);
        return DamageTaken;
    }

    When other characters have the function of causing damage to other characters: You need to create a TSubclassOf<class T> This is the inherent template of ue4:

    TSubclassOf<UDamageType> DamageTypeClass;

    Then call him when causing damage to the character:

    if (DamageTypeClass) {
      UGameplayStatics::ApplyDamage(monster, Attack_Power, nullptr, this, DamageTypeClass);
    }

    This will call TackDamage of the character that received the damage.

  • Show the enemy's health bar. This is relatively simple. First, create a HUD and then add it to the PlayerController and display it in the window. The problem is how to display it directly above the enemy and have a reasonable size: We can obtain the enemy's coordinates and then continuously obtain new coordinates in the Tick overridden by the class PlayerController, and then convert them to the 2d coordinates of the screen for display:

    if (MonsterHpBar) {
      FVector2D ScreenPosition;
    
      /* *Convert 3d to screen 2d coordinates */
      ProjectWorldLocationToScreen(showLocation, ScreenPosition);
    
      FVector2D ProgressBarSize(200.f, 25.f);
    
      MonsterHpBar->SetPositionInViewport(ScreenPosition);
      MonsterHpBar->SetDesiredSizeInViewport(ProgressBarSize);
    }
  • Save and load the game. Need to inherit a new C++ class USaveGame, you can create a structure in it and let it save the current character information. For example like this:

    USTRUCT(BlueprintType)
    struct FCharacterState {
      GENERATED_BODY()
    
      UPROPERTY(VisibleAnywhere, Category = "SaveGameData")
      float CurHp;
    
      UPROPERTY(VisibleAnywhere, Category = "SaveGameData")
      float MaxHp;
    
      UPROPERTY(VisibleAnywhere, Category = "SaveGameData")
      float CurEp;
    
      UPROPERTY(VisibleAnywhere, Category = "SaveGameData")
      float MaxEp;
    
      UPROPERTY(VisibleAnywhere, Category = "SaveGameData")
      int32 CoinCnt;
    
      UPROPERTY(VisibleAnywhere, Category = "SaveGameData")
      FVector Location;
    
      UPROPERTY(VisibleAnywhere, Category = "SaveGameData")
      FRotator Rotation;
    };

    So now I need to call him from MainPlayer and save and load information. You need to create new function bodies for saving and loading separately:

    /* *Save game function */
    void AMain::SaveGame() {
      USaveMyGame* SaveGame = Cast<USaveMyGame>(UGameplayStatics::CreateSaveGameObject(USaveMyGame::StaticClass()));
      SaveGame->CharacterState.CurHp = CurrentHp;
      SaveGame->CharacterState.MaxHp = MaxHp;
      SaveGame->CharacterState.CurEp = CurrentEp;
      SaveGame->CharacterState.MaxEp = MaxEp;
      SaveGame->CharacterState.CoinCnt = cntCoins;
      SaveGame->CharacterState.Location = GetActorLocation();
      SaveGame->CharacterState.Rotation = GetActorRotation();
    
      UGameplayStatics::SaveGameToSlot(SaveGame, SaveGame->GameName, SaveGame->PlayerIndex);
    }
    
    /* *Load game function */
    void AMain::LoadGame(bool bLoad) {
      USaveMyGame* LoadGame = Cast<USaveMyGame>(UGameplayStatics::CreateSaveGameObject(USaveMyGame::StaticClass()));
      USaveMyGame* LoadGameInstance = Cast<USaveMyGame>(UGameplayStatics::LoadGameFromSlot(LoadGame->GameName, LoadGame->PlayerIndex)); //Load the instance
      if (LoadGameInstance == nullptr) return;
    
      CurrentHp = LoadGameInstance->CharacterState.CurHp;
      MaxHp = LoadGameInstance->CharacterState.MaxHp;
      CurrentEp = LoadGameInstance->CharacterState.CurEp;
      MaxEp = LoadGameInstance->CharacterState.MaxEp;
      cntCoins = LoadGameInstance->CharacterState.CoinCnt;
    
      //To enter a new level, there is no need to load the position and rotation angle of the previous level!
      if (bLoad) {
          SetActorLocation(LoadGameInstance->CharacterState.Location);
          SetActorRotation(LoadGameInstance->CharacterState.Rotation);
      }
    }
  • Pause the window. Like the previous HUD windows, they are all set in PlayerController. The difference is how to display the HUD window more beautifully is very important. You can design its animation in the HUD blueprint class and implement it in PlayController At the same time the window is opened/closed, the HUD animation is called.
    The difference is that C++ implemented functions are called in the blueprint (but called events in the blueprint). We want to implement different functions, so we must add _Implementation at the end of the C++ function in the .cpp file to achieve an effect similar to the rewriting of the parent class. Of course, you also need to implement the call of the parent class in the blueprint.
    image The following is about the interaction and hiding of the mouse, it is very simple and not too much description:

    /* *Open mouse interaction */
    FInputModeGameAndUI InputModeGameAndUI;
    SetInputMode(InputModeGameAndUI);
    
    /* *Display mouse */
    bShowMouseCursor = true;
    
    /* *Turn off mouse interaction */
    FInputModeGameOnly InputModeGameOnly;
    SetInputMode(InputModeGameOnly);
    
    /* *Turn off the mouse */
    bShowMouseCursor = false;
  • Level transfer. The principle is very simple. You need to create a class LevenSend that inherits AActor, create a UBoxComponent in the class, let the box detect collision and call the function and switch the level.

    void AMain::SwitchLeven(FName nextLeven)
    {
      UWorld* world = GetWorld();
      FString curName = world->GetMapName();
      FName CurrentName(curName);
      if (CurrentName != nextLeven) {
        UGameplayStatics::OpenLevel(world, nextLeven);
      }
    }

    Need to pass in a FName which is the name of the next level, then we need to create a FName in LevenSend and let him customize it in the blueprint.

  • Cutscenes. This is more troublesome to talk about, and there is no good way to express it. It is easier to learn directly by watching the video. Clipscenes

Learning and Communication

wechat

ue-fps-rebirth's People

Contributors

sugar0612 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.