// テキストチャットの1要素で使うGUIの情報管理クラス // 料理で例えるなら食器 // メッセージ1つを構成するGUI要素:発言したユーザー名とそのアイコン、吹き出し画像、タイムスタンプテキスト、メッセージ内容[文章/画像/エモート画像] class TCMessageGUIActor { // hsCanvasIsPortrait()準拠で縦/横識別のフラグ const bool BOOL_FLAG_PORTRAIT = true; const bool BOOL_FLAG_LANDSCAPE = false; string _layerName; // 取り扱うGUI要素の一覧 string _guiNameAvatarIcon; string _guiNameSenderName; string _guiNameChatBalloon; string _guiNameContent; string _guiNameTimeStamp; // フォントサイズ設定 const int DEFAULT_LANDSCAPE_FONTSIZE = 15; const int DEFAULT_PORTRAIT_FONTSIZE = 50; bool _isSelfMessage; HS2DI _laAnchorPos; HS2DI _poAnchorPos; public TCMessageGUIActor() { _layerName = ""; _guiNameAvatarIcon = "AvatarIcon_button:"; _guiNameSenderName = "SenderName_text:"; _guiNameChatBalloon = "ChatBalloon_image:"; _guiNameContent = "content:"; _guiNameTimeStamp = "timeStamp_text:"; _isSelfMessage = false; _laAnchorPos = new HS2DI(); _poAnchorPos = new HS2DI(); } public void SetMessageUUID(string uuid){ _guiNameAvatarIcon = "AvatarIcon_button:%s" % uuid; _guiNameSenderName = "SenderName_text:%s" % uuid; _guiNameChatBalloon = "ChatBalloon_image:%s" % uuid; _guiNameContent = "content:%s" % uuid; _guiNameTimeStamp = "timeStamp_text:%s" % uuid; } // 生成関連---------------------- // 1メッセージで必要な要素を一式生成 public void CreateMessageGUISet(string layerName, TCMessageGUIData contentData, bool selfMessageFlag){ _layerName = layerName; _isSelfMessage = selfMessageFlag; // 生成する順番はタイムスタンプ(一番下の要素)から CreateTimeStampText(ClampTimestamp(contentData.GetTimeStanp())); // 送信内容のタイプに応じてテキスト、画像などの出し分け switch(contentData.GetMessageTypeID()){ // テキストタイプの生成 case TC_MESSAGE_TYPE_ID_TEXT: CreateTextMessage(contentData.GetContentData()); break; case TC_MESSAGE_TYPE_ID_EMOTE: break; case TC_MESSAGE_TYPE_ID_IMAGE: break; } CreateTextBalloon(selfMessageFlag); // 送信者名 CreateSenderName(contentData.GetSenderName(), selfMessageFlag); // アイコン画像生成 CreteGUIIcon(contentData.GetSenderIconURL()); } // アイコン画像生成 private void CreteGUIIcon(string iconImageURL){ // 横画面 HSGUIModel laGuiModel = CreateButtonGUIModel(_guiNameAvatarIcon, iconImageURL); laGuiModel.SetSize(MakeHS2DI(50, 50)); // 縦画面 HSGUIModel poGuiModel = CreateButtonGUIModel(_guiNameAvatarIcon, iconImageURL); poGuiModel.SetSize(MakeHS2DI(120, 120)); poGuiModel.SetAnchor("CM"); // 登録 hsCanvasAddGUI(_layerName, BOOL_FLAG_LANDSCAPE, laGuiModel); hsCanvasAddGUI(_layerName, BOOL_FLAG_PORTRAIT, poGuiModel); } // 送信者名 private void CreateSenderName(string senderName, bool selfMessageFlag){ // 横画面 HSGUIModel laGuiModel = CreateTextGUIModel(_guiNameSenderName, senderName, DEFAULT_LANDSCAPE_FONTSIZE); laGuiModel.SetSize(MakeHS2DI(255, DEFAULT_LANDSCAPE_FONTSIZE)); if(selfMessageFlag){ // テキスト用モデルの生成 HSTextModel laTextModel = MakeHSTextModel(senderName, DEFAULT_LANDSCAPE_FONTSIZE); laTextModel.SetAlignment("RM"); laGuiModel.SetTextModel(laTextModel); } // 縦画面 HSGUIModel poGuiModel = CreateTextGUIModel(_guiNameSenderName, senderName, DEFAULT_PORTRAIT_FONTSIZE); poGuiModel.SetSize(MakeHS2DI(1000, DEFAULT_PORTRAIT_FONTSIZE)); poGuiModel.SetAnchor("CM"); if(selfMessageFlag){ // テキスト用モデルの生成 HSTextModel poTextModel = MakeHSTextModel(senderName, DEFAULT_PORTRAIT_FONTSIZE); poTextModel.SetAlignment("RM"); poGuiModel.SetTextModel(poTextModel); } // 登録 hsCanvasAddGUI(_layerName, BOOL_FLAG_LANDSCAPE, laGuiModel); hsCanvasAddGUI(_layerName, BOOL_FLAG_PORTRAIT, poGuiModel); } // フキダシ生成 private void CreateTextBalloon(bool selfMessageFlag){ string chatBallonImagePath = "gui2024/chat/chat_balloon.png"; string poChatBallonImagePath = "gui2024/chat/chat_balloon_po.png"; if(selfMessageFlag){ chatBallonImagePath = "gui2024/chat/chat_balloon_self.png"; poChatBallonImagePath = "gui2024/chat/chat_balloon_po_self.png"; } // ---landscape--- // 9slice設定 HSImageModel laImageModel = MakeHSImageModel(chatBallonImagePath); HSRectLTRB imageLTRB = MakeHSRectLTRB(20, 10, 20, 10); laImageModel.SetLTRB(imageLTRB); // サイズの高さをテキストのサイズと同期 HS2DI chatBallonSize = MakeHS2DI(270, 35); // 生成 HSGUIModel laGuiModel = CreateCommonHSGUIMoel(_guiNameChatBalloon, "image"); laGuiModel.SetImageModel(laImageModel); laGuiModel.SetSize(chatBallonSize); // ---portrait--- // 9slice設定 HSImageModel poImageModel = MakeHSImageModel(poChatBallonImagePath); HSRectLTRB posImageLTRB = MakeHSRectLTRB(70, 25, 70, 25); poImageModel.SetLTRB(posImageLTRB); // サイズの高さをテキストのサイズと同期 chatBallonSize = MakeHS2DI(910, 145); HSGUIModel poGuiModel = CreateCommonHSGUIMoel(_guiNameChatBalloon, "image"); poGuiModel.SetImageModel(poImageModel); poGuiModel.SetSize(chatBallonSize); poGuiModel.SetAnchor("CM"); // 登録 hsCanvasAddGUI(_layerName, BOOL_FLAG_LANDSCAPE, laGuiModel); hsCanvasAddGUI(_layerName, BOOL_FLAG_PORTRAIT, poGuiModel); } // テキストメッセージのGUI生成 private void CreateTextMessage(string messageText){ // ---landscape--- HS2DI textcontentSize = MakeHS2DI(240, 35); // 本体生成 HSGUIModel laGuiModel = CreateCommonHSGUIMoel(_guiNameContent, "text"); laGuiModel.SetSize(textcontentSize); laGuiModel.SetZ(3); // テキスト用モデルの生成 HSTextModel laTextModel = MakeHSTextModel(messageText, DEFAULT_LANDSCAPE_FONTSIZE); HSColor fontColor = MakeHSColor(0.1, 0.1, 0.1 ,1); laTextModel.SetColor(fontColor); laTextModel.SetLineSpace(5); laTextModel.SetURLClickable(true); laGuiModel.SetTextModel(laTextModel); // ---portrait--- HS2DI poTextcontentSize = MakeHS2DI(810, 145); HSGUIModel poGuiModel = CreateCommonHSGUIMoel(_guiNameContent, "text"); poGuiModel.SetSize(poTextcontentSize); poGuiModel.SetZ(3); poGuiModel.SetAnchor("CM"); // テキスト用モデルの生成 HSTextModel poTextModel = MakeHSTextModel(messageText, DEFAULT_PORTRAIT_FONTSIZE); poTextModel.SetColor(fontColor); poTextModel.SetLineSpace(15); poTextModel.SetURLClickable(true); poGuiModel.SetTextModel(poTextModel); // 登録 hsCanvasAddGUI(_layerName, BOOL_FLAG_LANDSCAPE, laGuiModel); hsCanvasAddGUI(_layerName, BOOL_FLAG_PORTRAIT, poGuiModel); } // タイムスタンプ private void CreateTimeStampText(string timeStampText){ // 横画面 HSGUIModel laGuiModel = CreateTextGUIModel(_guiNameTimeStamp, timeStampText, DEFAULT_LANDSCAPE_FONTSIZE); laGuiModel.SetSize(MakeHS2DI(280, DEFAULT_LANDSCAPE_FONTSIZE)); // 縦画面 int fontBalance = -5; HSGUIModel poGuiModel = CreateTextGUIModel(_guiNameTimeStamp, timeStampText, DEFAULT_PORTRAIT_FONTSIZE + fontBalance); poGuiModel.SetSize(MakeHS2DI(300, DEFAULT_PORTRAIT_FONTSIZE)); poGuiModel.SetAnchor("CM"); // 登録 hsCanvasAddGUI(_layerName, BOOL_FLAG_LANDSCAPE, laGuiModel); hsCanvasAddGUI(_layerName, BOOL_FLAG_PORTRAIT, poGuiModel); } // レイアウト調整関連---------------------- // テキストのサイズを計算(Text生成後、1F待つ必要がある) public bool TextAretaReize(bool isPortrait){ // 横画面 int width,height = 0; hsGetTextAreaContentSize(_layerName, _guiNameContent, width, height); int textDefaultWidth = 240; int ballonDefaultWidth = 270; // テキスト本文とフキダシの余白 int whiteSpace = 25; if(isPortrait){ textDefaultWidth = 810; ballonDefaultWidth = 910; whiteSpace = 80; } HS2D contentSize = MakeHS2D(textDefaultWidth, height + whiteSpace); HS2D ballonSize = MakeHS2D(ballonDefaultWidth, height + whiteSpace); SetGUISize(_guiNameContent, contentSize, isPortrait); SetGUISize(_guiNameChatBalloon, ballonSize, isPortrait); return height > 0; } public void RefleshText(string message, bool isPortrait){ SetGUIText(_guiNameContent, message, isPortrait); } // このUIより上に作るメッセージの生成基準座標を取得 public TCMessageGUITransform GetPreMessageTransform(){ HS2DI laTopExtendOffset = GetGUISize(_guiNameChatBalloon, BOOL_FLAG_LANDSCAPE); HS2DI poTopExtendOffset = GetGUISize(_guiNameChatBalloon, BOOL_FLAG_PORTRAIT); int laAncorPosX = 0; if (hsSystemIsVRMode()) laAncorPosX = -390; HS2DI laIconSize = GetGUISize(_guiNameAvatarIcon, BOOL_FLAG_LANDSCAPE); HS2DI laAnchor = MakeHS2DI(laAncorPosX, _laAnchorPos.y() - laTopExtendOffset.y() - 35); HS2DI poIconSize = GetGUISize(_guiNameAvatarIcon, BOOL_FLAG_PORTRAIT); HS2DI poAnchor = MakeHS2DI(700, _poAnchorPos.y() - poTopExtendOffset.y() - 90); // ひとまとめにする TCMessageGUITransform preMessageAnchor = new TCMessageGUITransform(); preMessageAnchor.SetGUIAnchorPos(laAnchor, BOOL_FLAG_LANDSCAPE); preMessageAnchor.SetGUIAnchorPos(poAnchor, BOOL_FLAG_PORTRAIT); return preMessageAnchor; } // GUIの座標更新 public void RefleshGUIPos(HS2DI laAnchor, HS2DI poAnchor){ _laAnchorPos = laAnchor; _poAnchorPos = poAnchor; FollowGUIPos(laAnchor, poAnchor); } // アンカー座標を軸に各GUIを再配置 private void FollowGUIPos(HS2DI laAnchorPos, HS2DI poAnchorPos){ // タイムスタンプ HS2DI laTimeStampOffset = MakeHS2DI(125, 0); HS2DI poTimeStampOffset = MakeHS2DI(0, 0); if(_isSelfMessage){ laTimeStampOffset = MakeHS2DI(-145, 0); poTimeStampOffset = MakeHS2DI(-1010, 0); } HS2DI laTimeStampPos = MakeHS2DI(laAnchorPos.x() + laTimeStampOffset.x(), laAnchorPos.y() + laTimeStampOffset.y()); HS2DI poTimeStampPos = MakeHS2DI(poAnchorPos.x() + poTimeStampOffset.x(), poAnchorPos.y() + poTimeStampOffset.y()); SetGUIPos(_guiNameTimeStamp, laTimeStampPos, BOOL_FLAG_LANDSCAPE); SetGUIPos(_guiNameTimeStamp, poTimeStampPos, BOOL_FLAG_PORTRAIT); // 送信内容 HS2DI laContentOffset = MakeHS2DI(-115, -22); HS2DI poContentOffset = MakeHS2DI(-330, -1); if(_isSelfMessage){ laContentOffset = MakeHS2DI(-180, -22); poContentOffset = MakeHS2DI(-320, -1); } HS2DI laContentPos = MakeHS2DI(laAnchorPos.x() + laContentOffset.x(), laAnchorPos.y() + laContentOffset.y()); HS2DI poContentPos = MakeHS2DI(poAnchorPos.x() + poContentOffset.x(), poAnchorPos.y() + poContentOffset.y()); SetGUIPos(_guiNameContent, laContentPos, BOOL_FLAG_LANDSCAPE); SetGUIPos(_guiNameContent, poContentPos, BOOL_FLAG_PORTRAIT); // チャットバルーン HS2DI laBalloonOffset = MakeHS2DI(-105, -20); HS2DI poBalloonOffset = MakeHS2DI(-300, 0); if(_isSelfMessage){ laBalloonOffset = MakeHS2DI(-160, -20); poBalloonOffset = MakeHS2DI(-250, 0); } HS2DI laBalloonPos = MakeHS2DI(laAnchorPos.x() + laBalloonOffset.x(), laAnchorPos.y() + laBalloonOffset.y()); HS2DI poBalloonPos = MakeHS2DI(poAnchorPos.x() + poBalloonOffset.x(), poAnchorPos.y() + poBalloonOffset.y()); SetGUIPos(_guiNameChatBalloon, laBalloonPos, BOOL_FLAG_LANDSCAPE); SetGUIPos(_guiNameChatBalloon, poBalloonPos, BOOL_FLAG_PORTRAIT); // 上部の追加オフセット(チャットバルーンが可変である為) HS2DI laTopExtendOffset = GetGUISize(_guiNameChatBalloon, BOOL_FLAG_LANDSCAPE); HS2DI poTopExtendOffset = GetGUISize(_guiNameChatBalloon, BOOL_FLAG_PORTRAIT); // マイナスにすることでベクトルを上向きに変える laTopExtendOffset = MakeHS2DI(-laTopExtendOffset.x(), -laTopExtendOffset.y()); poTopExtendOffset = MakeHS2DI(-poTopExtendOffset.x(), -poTopExtendOffset.y()); // 送信者名 HS2DI laNameOffset = MakeHS2DI(-110, laTopExtendOffset.y() + -20); HS2DI poNameOffset = MakeHS2DI(-145, poTopExtendOffset.y() + -5); if(_isSelfMessage){ laNameOffset = MakeHS2DI(-170, laTopExtendOffset.y() + -20); poNameOffset = MakeHS2DI(-300, poTopExtendOffset.y() + -5); } HS2DI laNamePos = MakeHS2DI(laAnchorPos.x() + laNameOffset.x(), laAnchorPos.y() + laNameOffset.y()); HS2DI poNamePos = MakeHS2DI(poAnchorPos.x() + poNameOffset.x(), poAnchorPos.y() + poNameOffset.y()); SetGUIPos(_guiNameSenderName, laNamePos, BOOL_FLAG_LANDSCAPE); SetGUIPos(_guiNameSenderName, poNamePos, BOOL_FLAG_PORTRAIT); // アイコン HS2DI laIconOffset = MakeHS2DI(-375, laTopExtendOffset.y() + 20); HS2DI poIconOffset = MakeHS2DI(-1200, poTopExtendOffset.y() + 75); if(_isSelfMessage){ laIconOffset = MakeHS2DI(-110, laTopExtendOffset.y() + 20); poIconOffset = MakeHS2DI(-135, poTopExtendOffset.y() + 75); } HS2DI laIConPos = MakeHS2DI(laAnchorPos.x() + laIconOffset.x(), laAnchorPos.y() + laIconOffset.y()); HS2DI poIConPos = MakeHS2DI(poAnchorPos.x() + poIconOffset.x(), poAnchorPos.y() + poIconOffset.y()); SetGUIPos(_guiNameAvatarIcon, laIConPos, BOOL_FLAG_LANDSCAPE); SetGUIPos(_guiNameAvatarIcon, poIConPos, BOOL_FLAG_PORTRAIT); } // 消滅処理関連---------------------- public void Release() { ReleaseGUIContent(BOOL_FLAG_PORTRAIT); ReleaseGUIContent(BOOL_FLAG_LANDSCAPE); } private void ReleaseGUIContent(bool IsPortrait) { hsReleaseGUI(_layerName, IsPortrait, _guiNameAvatarIcon); hsReleaseGUI(_layerName, IsPortrait, _guiNameChatBalloon); hsReleaseGUI(_layerName, IsPortrait, _guiNameSenderName); hsReleaseGUI(_layerName, IsPortrait, _guiNameTimeStamp); hsReleaseGUI(_layerName, IsPortrait, _guiNameContent); } // 汎用使いまわし関数関連---------------------- // GUI座標の変更 private void SetGUIPos(string guiName, HS2DI position, bool usePrtraitFlag){ Layer layer = new Layer(); if(usePrtraitFlag){ layer = hsLayerGetPortrait(_layerName); }else{ layer = hsLayerGetLandscape(_layerName); } layer.SetGUIPos(guiName, position.x(), position.y()); } // GUIのスケール取得 private void SetGUISize(string guiName, HS2D size, bool usePrtraitFlag){ Layer layer = new Layer(); if(usePrtraitFlag){ layer = hsLayerGetPortrait(_layerName); }else{ layer = hsLayerGetLandscape(_layerName); } layer.SetGUISize(guiName, size.x(), size.y()); } // GUIのスケール取得 private HS2DI GetGUISize(string guiName, bool usePrtraitFlag){ Layer layer = new Layer(); if(usePrtraitFlag){ layer = hsLayerGetPortrait(_layerName); }else{ layer = hsLayerGetLandscape(_layerName); } Vector3 size = new Vector3(); layer.GetGUISize(guiName, size.x, size.y); return MakeHS2DI(size.x, size.y); } // GUI座標の変更 private void SetGUIText(string guiName, string text, bool usePrtraitFlag){ Layer layer = new Layer(); if(usePrtraitFlag){ layer = hsLayerGetPortrait(_layerName); }else{ layer = hsLayerGetLandscape(_layerName); } layer.CanvasSetGUIText(guiName, text); } // どのタイプの要素でも共通するセットアップ private HSGUIModel CreateCommonHSGUIMoel(string modelName, string modelType){ HSGUIModel model = new HSGUIModel(); model.SetName(modelName); model.SetShow(true); model.SetType(modelType); int defaultZ = 1; model.SetZ(defaultZ); HS2D pivot = new HS2D(); pivot.SetXY(1, 1); model.SetPivot(pivot); model.SetAnchor("RM"); return model; } // 画像要素のセットアップ public HSGUIModel CreateImageGUIModel(string modelName, string imageFilePath){ HSGUIModel imageGUI = CreateCommonHSGUIMoel(modelName, "image"); HSImageModel imageModel = MakeHSImageModel(imageFilePath); imageGUI.SetImageModel(imageModel); return imageGUI; } // ボタン要素のセットアップ public HSGUIModel CreateButtonGUIModel(string modelName, string imageFilePath){ HSGUIModel buttonGUI = CreateCommonHSGUIMoel(modelName, "button"); HSButtonModel buttonModel = new HSButtonModel(); buttonModel.SetFileName(imageFilePath); buttonGUI.SetButtonModel(buttonModel); return buttonGUI; } // テキスト要素のセットアップ public HSGUIModel CreateTextGUIModel(string modelName, string text, int fontSize){ HSGUIModel textGUI = CreateCommonHSGUIMoel(modelName, "text"); HSTextModel textModel = MakeHSTextModel(text, fontSize); HSColor fontColor = MakeHSColor(0.9, 0.9, 0.9 ,1); textModel.SetColor(fontColor); textGUI.SetTextModel(textModel); return textGUI; } private HSTextModel MakeHSTextModel(string text, int fontSize){ HSTextModel textModel = new HSTextModel(); textModel.SetFontSize(fontSize); textModel.SetText(text); textModel.SetFontFamily("BIZUDPGothic"); textModel.SetAlignment("LM"); textModel.SetOverflowWrap(true); HSColor fontColor = MakeHSColor(0.9, 0.9, 0.9 ,1); textModel.SetColor(fontColor); return textModel; } private HSImageModel MakeHSImageModel(string imageFilePath){ HSImageModel imageModel = new HSImageModel(); imageModel.SetURI(imageFilePath); return imageModel; } private HS2D MakeHS2D(float x, float y){ HS2D hs2d = new HS2D(); hs2d.SetXY(x, y); return hs2d; } private HS2DI MakeHS2DI(int x, int y){ HS2DI hs2di = new HS2DI(); hs2di.SetXY(x, y); return hs2di; } private HSColor MakeHSColor(float r, float g, float b, float a){ HSColor hsColor = new HSColor(); hsColor.SetRGBA(r, g, b, a); return hsColor; } private HSRectLTRB MakeHSRectLTRB(int l, int t, int r, int b){ HSRectLTRB hsLTRB = new HSRectLTRB(); hsLTRB.SetLTRB(l, t, r, b); return hsLTRB; } // タイムスタンプの文字列を人間用に変換 private string ClampTimestamp(string src) { if(src.IsEmpty()) return src; // 年 string year = src.SubString(0, 4); // 月 string month = src.SubString(5, 2); // 日 string day = src.SubString(8, 2); // 時 string hour = src.SubString(11, 2); // 取得データとの時差が9時間あるようなのでそちらを考慮する int hour_integer = hour.ToInt(); hour_integer += 9; hour = "%d" % hour_integer; // 分 string minutes = src.SubString(14, 2); // 日時テキストを作成 // string dst = year + "/" + month + "/" + day + " " + hour + ":" + minutes; // レイアウトやデザインが変わるのでひとまず時間だけ表示しておく string dst = hour + ":" + minutes; return dst; } }