Zego即构是一家做直播的服务商,Zego即构自己的房间列表,本文只是测试功能用,相应代码并没完全测试,请选择性参考。
我们在UE4中来实现一下,我感觉这个过程有点意思,UE4中C++与蓝图和UI的互相通信基本全部用到了。
Zego即构没有专门的UE4插件,所以我们主要逻辑全部在C++中,蓝图只是辅助。
首先,我们定义一个房间结构,因为要想UE4中C++和蓝图可见可用,我们要用C++实现,并实现特定的写法让蓝图知道。
USTRUCT(BlueprintType) struct FRoomBlueprint { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "") FString roomId; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "") FString roomName; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "") FString anchorName; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "") int streamSize; public: FRoomBlueprint() { roomId = nullptr; roomName = nullptr; anchorName = nullptr; streamSize = ; } };
FRoomBlueprint
理一下逻辑,UE得到所有房间列表,然后给UE4蓝图,蓝图根据每个房间列表创建对应个房间,完成了。
我们找一下Zego即构的如何获得房间列表的API,发现他是通过Http Get方式得到的,因为Http Get我们肯定以异步方式来实现,那么如何通知蓝图了,我们需要定义一个蓝图能识别的事件委托才行。
using namespace ZEGO::LIVEROOM; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetRoomList, TArray<FRoomBlueprint>, roomList); UCLASS() class TESTJIGOU_API AJigouMain : public AActor, public IZegoVideoRenderCallback { GENERATED_BODY() public: // Sets default values for this actor's properties AJigouMain(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; public: // Called every frame virtual void Tick(float DeltaTime) override; ]); UPROPERTY(BlueprintAssignable) FOnGetRoomList eventGetRoomList; UFUNCTION(BlueprintCallable, Category = "AMRVideo") void PullRoomList(); }; void AJigouMain::PullRoomList() { FString baseUri = FString::Printf(_T("https://liveroom%u-api.zego.im/demo/roomlist?appid=%u"), ManagerConnect::Get().GetAppID(), ManagerConnect::Get().GetAppID()); auto HttpRequest = FHttpModule::Get().CreateRequest(); HttpRequest->SetVerb("GET"); HttpRequest->SetHeader("Content-Type", "application/json"); HttpRequest->SetURL(baseUri); //HttpRequest->SetContentAsString(JsonStr); HttpRequest->OnProcessRequestComplete().BindLambda( [&](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { if (Response.Get() == nullptr) return; auto content = Response->GetContentAsString(); TSharedPtr<FJsonObject> JsonObject; auto Reader = TJsonReaderFactory<>::Create(content); if (FJsonSerializer::Deserialize(Reader, JsonObject)) { auto state = JsonObject->GetIntegerField("code"); ) { TArray<FRoomBlueprint> roomArray; auto data = JsonObject->GetObjectField("data"); auto roomList = data->GetArrayField("room_list"); int lenght = roomList.Num(); ; i < lenght; i++) { FRoomBlueprint rInfo = {}; auto room = roomList[i]->AsObject(); rInfo.roomId = room->GetStringField("room_id"); rInfo.roomName = room->GetStringField("room_name"); rInfo.anchorName = room->GetStringField("anchor_id_name"); auto listStream = room->GetArrayField("stream_info"); ) { rInfo.streamSize = listStream.Num(); roomArray.Add(rInfo); } } if (eventGetRoomList.IsBound()) { eventGetRoomList.Broadcast(roomArray); } } } }); HttpRequest->ProcessRequest(); }
AJigouMain
其实如果我们不直接用C++实例,再实现一个蓝图继承AJigouMain,然后实现UFUNCTION(BlueprintNativeEvent)也一样可以得到这个效果,再这,我们就直接把如上AJigouMain实例结果放入场景中。
嗯,逻辑部分差不多了,我们开始实现UI,首先我们定义一个房间控件。
嗯,记的我们前面声明的结构FRoomBlueprint吗,一个房间对应一个FRoomBlueprint,前面文本分别绑定对应FRoomBlueprint的流数,房间名。
我们要考虑的是点击加入后如何把当前房间的信息发散出去,简单来说,调用一个事件,事件要包含当前的点击的房间信息,至于事件绑定的执行函数,我们现在还不知道,也不用考虑。
简单来说,Room这个房间控件知道的就是,我点击了我的加入button,并在点击后,调用OnJoin,调用当前事件把对应的FRoomBlueprint结构传入事件中,这个控件就不需要管别的事了。
如上,得到所有房间后,需要根据得到的房间列表生成对应的房间列表UI,有如上的房间控件后,我们来看看UE4如何生成列表。
如上主要逻辑都在UIPanel里,Room List box就是一个UE4里的Vertical Box控件,用来垂直放入我们上面的Room控件,并把每个FRoomBlueprint实例传入对应的Room控件,在这个集中门面类中,我们知道对应的Room控件的加入功能具体能做什么了,绑定有相同的函数签名的事件上就行了。并且结合上面的eventGetRoomList事件,我们调用这个方法就行了。
对应的加入房间,我们提供一个简单的UBlueprintFunctionLibrary类,帮我们处理一些基本的C++事件,如:
UCLASS() class TESTJIGOU_API UBlueprintHelper : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION(BlueprintPure, Category = "AJigouMain") static bool OnJoinRoom(FRoomBlueprint room, FString userID); UFUNCTION(BlueprintPure, Category = "AJigouMain") static TArray<FDeviceInfo> GetDeviceList(); UFUNCTION(BlueprintPure, Category = "AJigouMain") static bool OnCreateRoom(FString userID); UFUNCTION(BlueprintPure, Category = "AJigouMain") static bool SetDeviceRoom(FString deviceID); UFUNCTION(BlueprintPure, Category = "AJigouMain") static bool SetDeviceRoom2(FString deviceID); UFUNCTION(BlueprintPure, Category = "AJigouMain") static bool SetRole(int roleID); UFUNCTION(BlueprintPure, Category = "AJigouMain") static FString GetRoleName(); UFUNCTION(BlueprintPure, Category = "AJigouMain") static bool PullStream(FString stream); }; bool UBlueprintHelper::OnJoinRoom(FRoomBlueprint room, FString userID) { auto currentRole = ManagerConnect::Get().getCurrentRole(); FTCHARToUTF8 user(*(currentRole.userID)); FString roomID = room.roomId; FString roomName = room.roomName; auto bJoin = LIVEROOM::SetUser(user.Get(), user.Get()); if (bJoin) { FTCHARToUTF8 id(*roomID); FTCHARToUTF8 roomName(*roomName); auto bJoin = LIVEROOM::LoginRoom(id.Get(), currentRole.role, roomName.Get()); ManagerConnect::Get().bAuthor = false; } return bJoin; }
倒数第二张图上,点击加入按键,绑定的方法就是这个方法。
最后效果图:
和直播出来画面不一样是因为我们在UE4里做了反畸变,用的是in.yml生成的一张UV贴图,需要注意的是,此处这图贴图最好用PF_FloatRGBA,不要用R8G8B8A8,精度不够,生成的图片很多裂变,PF_FloatRGBA对应的是FFloat16Color,而不是FLineColor。