#1. 코딩 컨밴션
1. 개념
코딩 컨밴션은 특정 프로그래밍 언어나 프로젝트, 팀에서 코드를 어떻게 작성할지에 대한 규칙이나 가이드라인을 의미합니다. 이는 변수명, 함수명의 명명 법, 들여 쓰기의 크기, 코드 라인의 길이, 주석의 방법 등 다양한 측면을 포함할 수 있습니다.
2. 장점, 왜 필요한가?
코드 컨밴션을 통해 그룹 내 협업 과정에서 일관된 코드 스타일을 유지할 수 있도록 해줍니다. 이를 통해, 기존 코드의 확장 혹은 수정 작업이 용이해지며, 원활한 코드 리뷰가 가능해집니다. 정리하면, 코드 컨밴션은 코드의 일관성을 유지할 수 있도록 도와주며, 이는 코드의 가독성과 유지보수성 향상에 기여합니다. 이를 통해, 그룹 내 협업이 효율적으로 이루어집니다.
3. 코드 컨밴션 확립 방법 및 과정
- 가이드라인 선정 : Unreal에서 제공하는 기존의 코드 컨밴션을 기반으로 팀 특성에 맞는 커스텀 규칙을 정합니다.
- 문서 작성 : 결정된 코드 컨밴션은 모든 팀원이 접근할 수 있는 문서로 작성하며, 해당 문서는 프로젝트 진행 과정에서 지속적으로 업데이트합니다.
- 코드 리뷰 : 정기적으로 코드 리뷰를 실시하여, 코드 컨밴션 준수 여부를 확인하고 피드백을 서로에게 제공합니다.
#2. 가이드라인
1. 명명 방법 및 기본 세팅 관련 커스텀 규칙
1. #include
// 0. 헤더 파일은 최소화, 필요 없는 헤더 파일은 제거
#include "GameFramework/CharacterMovementComponent.h" // 제거!
#include "04_Component/BaseCharacterMovementComponent.h" // 해당 헤더파일에서 이미 위 헤더파일을 #include 하고 있음
// 1. .h 파일 상단에 #pragma once 작성 (중복 #include를 방지)
#pragma once
// 2. 내부 파일 헤더 및 Log 관련 헤더를 먼저 입력
#include "PlayerCharacter.h"
#include "Logging/StructuredLog.h"
// 3. 관련 내용 끼리 묶어 놓고, 각 묶음을 개행으로 구분
#include "04_Component/BaseInputComponent.h" // 해당 클래스의 Default Coomponent들
#include "04_Component/BaseCharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Components/CapsuleComponent.h"
#include "Camera/CameraComponent.h"
#include "03_Player/BasePlayerController.h" // 해당 클래스의 함수 본문에서 활용하는 클래스들
#include "03_Player/PlayerStateBase.h"
#include "04_Component/BaseAbilitySystemComponent.h"
#include "Kismet/KismetMathLibrary.h" // Math 관련 라이브러리
2. 변수 명명 규칙
// 1. Pascal Case 활용 : 각 단어를 대문자로 구분하는 명명 방법
int SomeInt = 3;
int CurrentInt = 4;
// 2. bool 형은 접두어 'b' 붙이기
bool bFalling = false;
bool bMoving = false;
// 3. float 형은 정수의 소수점 뒤 접미어 'f'붙이기
float SomeFloat = 3.f;
float SomeFloat2 = 2.5f;
// 4. 열거형은 접두어 'E' 붙이기
enum class EPlayerState : uint8
{
//...
};
enum class EMovementState : uint8
{
//...
};
// 5. 구조체는 접두어 'F' 붙이기
struct FBaseAbilitySet
{
//...
};
3. 함수 명명 규칙
// 1. Pascal Case & 동사+목적어 조합 : 함수 명명 규칙 및 인자(Param)에도 동일하게 적용.
UCLASS()
class AGEOFWOLVES_API UInputConfig : public UDataAsset
{
//...
UFUNCTION(BlueprintCallable, Category = "Input | Input Action")
const UInputAction* FindNativeInputActionForTag(const FGameplayTag& InputTag, bool bLogNotFound = true) const;
UFUNCTION(BlueprintCallable, Category = "Input | Input Action")
const UInputAction* FindAbilityInputActionForTag(const FGameplayTag& InputTag, bool bLogNotFound = true) const;
//...
}
// 2. Out Parameter의 경우 인자명 앞에 접두어 'Out' 붙여주기
UCLASS()
class AGEOFWOLVES_API UBaseAbilitySet : public UPrimaryDataAsset
{
//...
void GiveStartupAttributeSetToAbilitySystem(UBaseAbilitySystemComponent* ASC, FBaseAbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject) const;
//...
};
// 3. 이벤트(Delegate) 타입은 접두어 'F'를 붙여주고, 선언 시 'On'을 붙여준다.
DECLARE_DYNAMIC_DELEGATE(FPlayerInputInit); // 'F + ~' 조합
class SomeClass
{
public:
FPlayerInit OnPlayerInit // 'On + ~' 조합
};
// 4. Template 함수의 경우 클래스 내부에서 선언과 정의를 함께 작성
class SomeClass
{
//...
// @brief : Tempalte function in which Native Input Action matching to the Input Tag will bind to User's Callback Function
template<typename UserClass, typename FuncType>
void BindNativeInputAction(const UInputConfig* InputConfig, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent, UserClass* Object, FuncType Func)
{
check(InputConfig);
if (const auto& InputAction = InputConfig->FindNativeInputActionForTag(InputTag))
{
BindAction(InputAction, TriggerEvent, Object, Func);
}
}
};
// 5. 지역 변수는 사용처와 최대한 가까운 곳에 정의, 최대한 늦게 정의
void APlayerCharacter::AdjustControllerRotation(float DeltaSeconds)
{
check(DirectionCurve);
// 캐릭터의 현재 가속도 벡터를 기반으로 한 로테이션을 계산
FRotator Rotation1 = UKismetMathLibrary::MakeRotFromX(GetCharacterMovement()->GetCurrentAcceleration());
// 캐릭터가 바라보는 방향의 벡터를 기반으로 로테이션을 계산
FRotator Rotation2 = GetControlRotation();
// 두 방향 사이의 최소 각도 차이를 계산
float OffsetAngle = DirectionCurve->GetFloatValue(FMath::FindDeltaAngleDegrees(Rotation1.Yaw, Rotation2.Yaw));
FRotator Rotaion3 = UKismetMathLibrary::MakeRotator(0.f, 0.f, GetControlRotation().Yaw + OffsetAngle);
// 선형 보간
FRotator TargetRotation = UKismetMathLibrary::RInterpTo(GetActorRotation(), Rotaion3, DeltaSeconds, 10.f);
SetActorRotation(TargetRotation);
}
4. 주석 작성 규칙
// 1. [ @목적, @설명, @참조 ] 기본 구조, 목적과 설명은 필수, 참조는 선택
#pragma region Controller Rotaion
protected:
/*
* @목적 : 캐릭터 객체의 회전 값을 Controller의 회전 값에 따른 보간 작업을 수행합니다.
* @설명 : 사용자 카메라가 바라보는 방향과 캐릭터 객체가 바라보는 방향의 조정 작업을 수행합니다.
* @참조 : -
*/
void AdjustControllerRotation(float DeltaSeconds);
/*
* @목적 : 사용자의 카메라 회전에 따른 캐릭터 객체의 회전 보간 작업
* @설명 : Float Curve를 통해 카메라(Controller) 회전 값과 캐릭터 이동 방향의 회전 값
* @참조 : APlayerCharacter::AdjustControllerRotation
*/
UPROPERTY(EditDefaultsOnly, Category = "Character Movement|Control Rotation")
UCurveFloat* DirectionCurve;
#pragma endregion
2. 클래스 관련 규칙
1. 헤더 파일(. h) 구조
// 1. #include
// 2. Delegate 선언
// 3. 전방 선언
// 4. 열거형
// 5. 구조체
// 6. 클래스
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTagContainer.h"
#include "InputConfig.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogInputConfig, Log, All)
class UInputAction;
class UInputMappingContext;
USTRUCT(BlueprintType)
struct FInputActionInfo
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Meta = (Categories = "InputTag"))
FGameplayTag InputTag;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TObjectPtr<UInputAction> InputAction = nullptr;
};
/**
*
*/
UCLASS()
class AGEOFWOLVES_API UInputConfig : public UDataAsset
{
GENERATED_BODY()
public:
UInputConfig(const FObjectInitializer& ObjectInitializer);
UFUNCTION(BlueprintCallable, Category = "Input | Input Action")
const UInputAction* FindNativeInputActionForTag(const FGameplayTag& InputTag, bool bLogNotFound = true) const;
UFUNCTION(BlueprintCallable, Category = "Input | Input Action")
const UInputAction* FindAbilityInputActionForTag(const FGameplayTag& InputTag, bool bLogNotFound = true) const;
public:
// IMC
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input | Input Mapping Context")
TObjectPtr<UInputMappingContext> InputMappingContext;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input | Input Mapping Context")
int32 MappingPriority;
// List of input actions used by the owner. These input actions are mapped to a gameplay tag and must be manually bound.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input | Native Input Action")
TArray<FInputActionInfo> NativeInputActions;
// List of input actions used by the owner. These input actions are mapped to a gameplay tag and are automatically bound to abilities with matching input tags.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input | Ability Input Action")
TArray<FInputActionInfo> AbilityInputActions;
};
- #include, generated.h(#include "InputConfig.generated.h") 코드는 개행하여 구분해 줍니다. 이 밑으로 헤더파일을 작성할 경우 에러 발생!
- Delegate
- 전방 선언, 구조체 내부 데이터 필드에서 활용될 클래스 유형 외 다른 모든 클래스 유형은 전방 선언하고, 헤더 파일 내부에서 #include 하지 않도록 합니다. 어차피 본문 파일(. cpp)에서 #include 하니까!
- 열거형, 열거형의 경우 구조체 내부 데이터 필드에서 활용될 가능성이 높기 때문에, 구조체 보다 먼저 작성
- 구조체
- 클래스
2. 클래스 본문 기본 구조
/*
0. 큰 영역 : #pragma region [영역_이름] ~ #pragma endregion
1. 세부 영역 : //~[영역_이름] ~ //~End Of [영역_이름]
2. Default Setting 영역 : 클래스가 상속중인 상위 클래스의 인터페이스 함수들의 오버라이딩 선언
3. Custom 영역 : 클래스 고유의 동작들을 수행하는 멤버 함수 및 데이터 멤버 정의
*/
UCLASS()
class AGEOFWOLVES_API APlayerCharacter : public ACharacterBase
{
GENERATED_BODY()
#pragma region Default Setting
public:
// Sets default values for this character's properties
APlayerCharacter(const FObjectInitializer& ObjectInitializer);
protected:
//~AActor interface
virtual void PostInitializeComponents() override;
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void Tick(float DeltaSeconds) override;
//~End Of AActor interface
//~APawn interface
virtual void PossessedBy(AController* NewController) override;
virtual void PawnClientRestart() override;
//~End Of APawn Interface.
//~Components
UPROPERTY(VisibleAnywhere, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FollowCamera;
//~End Of Components
#pragma endregion
#pragma region Controller Rotaion
protected:
/*
* @목적 : 캐릭터 객체의 회전 값을 Controller의 회전 값에 따른 보간 작업을 수행합니다.
* @설명 : 사용자 카메라가 바라보는 방향과 캐릭터 객체가 바라보는 방향의 조정 작업을 수행합니다.
* @참조 : -
*/
void AdjustControllerRotation(float DeltaSeconds);
/*
* @목적 : 사용자의 카메라 회전에 따른 캐릭터 객체의 회전 보간 작업을 수행합니다.
* @설명 : Float Curve를 통해 캐릭터 회전 값 보간 작업 과정에 필요한 Offset 값을 가져옵니다.
* @참조 : APlayerCharacter::AdjustControllerRotation
*/
UPROPERTY(EditDefaultsOnly, Category = "Character Movement|Control Rotation")
UCurveFloat* DirectionCurve;
#pragma endregion
};
큰 영역은 #pragma region [영역_이름] ~ #pragma endregion으로 나눕니다. 세부 영역은(상위 클래스의 인터페이스 함수들만) //~[영역_이름] ~ //~End Of [영역_이름]으로 나눕니다. 큰 영역은 필수, 세부 영역은 편의대로 작성
3. 그 외 규칙
// 1. 클래스의 데이터 멤버에 직접적으로 접근하지 않습니다! 꼭, Getter&Setter 활용
FORCEINLINE float GetRotationRate() { return RotationRate; };
// 앞으로 추가될 내용 아래 작성
'그룹프로젝트 > PM' 카테고리의 다른 글
[GroupProject_AOW]#2. GitHub Policy (0) | 2024.04.09 |
---|