游戏规则
电脑随机生成含有偶数个卡片的矩阵,玩家点击卡片,卡片翻转展示正面的图案,翻开下一张卡片时,如果两张卡片图案相同,则消除,图片不同则两张卡片翻转会原来的状态。
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 public : ACard (); UFUNCTION (BlueprintCallable) void Flip () ; UFUNCTION (BlueprintCallable) void Flipping (float Z) ; UFUNCTION (BlueprintCallable) void FlipOver () ; UFUNCTION (BlueprintCallable) bool IsFlipped () const ; UFUNCTION (BlueprintCallable) FString GetCardID () const ; UFUNCTION (BlueprintCallable) void SetCardID (const FString& NewID) ; UPROPERTY (EditAnywhere) UStaticMeshComponent* CardMesh; protected : virtual void BeginPlay () override ; private : bool bIsFlipped; FString CardID;
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 ACard::ACard () { PrimaryActorTick.bCanEverTick = false ; CardMesh = CreateDefaultSubobject <UStaticMeshComponent>(TEXT ("Card Mesh" )); RootComponent = CardMesh; bIsFlipped = false ; } 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) { 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 ACardGameMode::ACardGameMode () { DefaultPawnClass = nullptr ; FlippedCardNum = 0 ; CardClass = ACard::StaticClass (); } void ACardGameMode::BeginPlay () { Super::BeginPlay (); StartGame (); } void ACardGameMode::StartGame () { CardPatterns.Empty (); CardGrid.Empty (); for (int32 i = 0 ; i < 8 ; ++i) { FString CardPattern = FString::Printf (TEXT ("Pattern_%d" ), i); CardPatterns.Add (CardPattern); CardPatterns.Add (CardPattern); } CardPatterns.Sort ([](const FString& A, const FString& B) { 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]); CardGrid.Add (NewCard); } } } } void ACardGameMode::CardClicked (ACard* ClickedCard) { UE_LOG (LogTemp, Warning, TEXT ("%s" ), *ClickedCard->GetCardID ()); if (ClickedCard->IsFlipped ()) { if (FlippedCardNum < 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就行了。
效果