【虚幻引擎5 III】做个翻卡片连连看

【虚幻引擎5 III】做个翻卡片连连看

anzai249 床主

游戏规则

电脑随机生成含有偶数个卡片的矩阵,玩家点击卡片,卡片翻转展示正面的图案,翻开下一张卡片时,如果两张卡片图案相同,则消除,图片不同则两张卡片翻转会原来的状态。

graph LR
    A[翻开第一个卡片] --> B[储存第一个卡片的ID];
    B --> C[翻开第二个卡片];
    C --> D{两张卡片ID是否相同?};
    D -- Yes --> E[卡片消除];
    D -- No --> F[两张卡片翻转回去];
    F --> A;

实现

篇幅原因只展示关键代码。

卡片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Card.h
public:
ACard();

UFUNCTION(BlueprintCallable)
void Flip(); // 改变翻转状态

UFUNCTION(BlueprintCallable)
void Flipping(float Z); // 翻转动画调用

UFUNCTION(BlueprintCallable)
void FlipOver(); // 翻转结束,其实用Finished这样的词更好

UFUNCTION(BlueprintCallable)
bool IsFlipped() const; // 返回是否翻转

UFUNCTION(BlueprintCallable)
FString GetCardID() const; // 返回卡片ID(图案)

UFUNCTION(BlueprintCallable)
void SetCardID(const FString& NewID); // 指定卡片ID

UPROPERTY(EditAnywhere)
UStaticMeshComponent* CardMesh; // 卡片模型

protected:
virtual void BeginPlay() override; // 开始播放

private:
bool bIsFlipped; // 是否翻转的bool
FString CardID; // 卡片ID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Card.cpp

ACard::ACard()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;

CardMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Card Mesh"));
RootComponent = CardMesh;

bIsFlipped = false;
}

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

}

void ACard::Flip()
{
UE_LOG(LogTemp, Warning, TEXT("Filp state changed"));
if (!bIsFlipped) // 简单的判断
{
bIsFlipped = true;
}
else
{
bIsFlipped = false;
}
}

void ACard::Flipping(float Z)
{
// 传入一个不断变化的Z之后给它赋值到卡片的旋转上
FRotator CardRotator = CardMesh->GetComponentRotation();
CardRotator.Yaw = Z;
CardMesh->SetWorldRotation(CardRotator);
}

void ACard::FlipOver()
{
ACardGameMode* GameModeRef = nullptr; // 定义和获得游戏模式
if (!GameModeRef)
{
UWorld* World = GetWorld();
if (World)
{
AGameModeBase* GameMode = UGameplayStatics::GetGameMode(World);
GameModeRef = Cast<ACardGameMode>(GameMode);
}
}

if (GameModeRef)
{
GameModeRef->CardClicked(this); // 调用游戏模式里的点击卡片函数
}
}

bool ACard::IsFlipped() const
{
return bIsFlipped;
}

FString ACard::GetCardID() const
{
return CardID;
}

void ACard::SetCardID(const FString& NewID)
{
CardID = NewID;
}

用蓝方块代替卡片,再弄一个灯光,之后告诉你目的。

操作灯光。

卡片翻转动画,需要先判断是否处于正在旋转的状态,如果不弄这个可能会出现怪怪的问题,比如说快速点击同一个卡片,它自己和自己匹配上也能消除/doge。然后就是判断它是否已经旋转,已经旋转的点击之后倒放,翻回来,没旋转的正放,旋转。Finished之后调用FlipOver()去GameMode里进行逻辑判断。

(然后我刚开始把Is Flipped的if连反了)

游戏模式

.h不分享了,直接.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// CardGameMode.cpp

ACardGameMode::ACardGameMode()
{
DefaultPawnClass = nullptr;
FlippedCardNum = 0;
CardClass = ACard::StaticClass();
}

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

// 游戏开始时调用 StartGame() 函数
StartGame();
}

void ACardGameMode::StartGame()
{
CardPatterns.Empty(); // 这俩是TArray,前者是存卡片ID(图案,FString)的,后者是存卡片(ACard*)的
CardGrid.Empty();

for (int32 i = 0; i < 8; ++i)
{
FString CardPattern = FString::Printf(TEXT("Pattern_%d"), i); // 生成ID
CardPatterns.Add(CardPattern);
CardPatterns.Add(CardPattern);
}

CardPatterns.Sort([](const FString& A, const FString& B) { // 打乱ID
return FMath::RandBool();
});

const int32 NumRows = 4; // 行列,记得乘出来是个偶数,不然最后会剩一个
const int32 NumCols = 4;

if (CardClass) // 判断一下给的卡片子类是否存在(不存在引擎会炸,别问我怎么知道的)
{
for (int32 Row = 0; Row < NumRows; ++Row)
{
for (int32 Col = 0; Col < NumCols; ++Col)
{
FTransform SpawnTransform(FRotator::ZeroRotator, FVector(0.00f, Row * 100.0f, 50 + Col * 100.0f), FVector::OneVector); // 分配坐标
ACard* NewCard = GetWorld()->SpawnActor<ACard>(CardClass, SpawnTransform.GetLocation(), FRotator::ZeroRotator); // 生成
NewCard->SetCardID(CardPatterns[Row * NumCols + Col]); // 分配ID
CardGrid.Add(NewCard); // 放到卡片序列里
}
}
}
}

void ACardGameMode::CardClicked(ACard* ClickedCard)
{
UE_LOG(LogTemp, Warning, TEXT("%s"), *ClickedCard->GetCardID());
if (ClickedCard->IsFlipped()) // 判断它是否已经翻过来了,因为这个是在最后调用的,所以要反着写。
{
if (FlippedCardNum < 1) // 原理同上,1的时候点击才会达到翻开两个消除。
{
FlippedCardNum++;
UE_LOG(LogTemp, Warning, TEXT("FlippedCardNum %d"), FlippedCardNum);
MemoryCard = ClickedCard; // 翻开一个的时候把它记下来
}
else
{
FlippedCardNum = 0; // 消除了,可以初始化翻开的卡片数
if (MemoryCard->GetCardID() == ClickedCard->GetCardID()) // 两个是否一样
{
ClickedCard->Destroy(); // 一样就消了
MemoryCard->Destroy();
MemoryCard = nullptr; // 清掉记下来的卡片
}
else
{
CardFilp(ClickedCard, MemoryCard); // 不一样就把两个都翻回去,这个函数在下面那张图里
MemoryCard = nullptr;
}
}
}
else
{
FlippedCardNum--; // 如果卡片是翻过来的,那么把它翻回去
UE_LOG(LogTemp, Warning, TEXT("FlippedCardNum %d"), FlippedCardNum);
MemoryCard = nullptr;
}
}

这是CardFilp(),一次性把这俩卡都翻回去。

要把这里指定为卡的子类,不然上面的if(CardClass)过不去。

其他

需要写一个允许监听鼠标点击和悬停事件的Controller,这里不多赘述。

没有美术师,所以没有模型和图案。判断图案全靠输出日志。如果想加图案也不难,直接往Card类里塞Mesh就行了。

效果

  • 标题: 【虚幻引擎5 III】做个翻卡片连连看
  • 作者: anzai249
  • 创建于 : 2023-08-17 19:01:28
  • 更新于 : 2023-08-20 22:30:44
  • 链接: https://anzai.sleepingbed.top/archives/posts/167165dd.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
【虚幻引擎5 III】做个翻卡片连连看