First pass on the image drawing
This commit is contained in:
126
Assets/Editor/Dialogue/DialogueContentDrawer.cs
Normal file
126
Assets/Editor/Dialogue/DialogueContentDrawer.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dialogue.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom property drawer for DialogueContent that displays either text or image fields based on content type
|
||||
/// </summary>
|
||||
[CustomPropertyDrawer(typeof(DialogueContent))]
|
||||
public class DialogueContentDrawer : PropertyDrawer
|
||||
{
|
||||
// Height constants
|
||||
private const float TypeSelectorHeight = 20f;
|
||||
private const float PropertySpacing = 2f;
|
||||
private const float TextFieldHeight = 40f; // Taller for multi-line text
|
||||
private const float ImageFieldHeight = 18f;
|
||||
private const float PreviewHeight = 64f;
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
var contentTypeProperty = property.FindPropertyRelative("_contentType");
|
||||
var height = TypeSelectorHeight + PropertySpacing;
|
||||
|
||||
// Add height based on content type
|
||||
if (contentTypeProperty.enumValueIndex == (int)DialogueContentType.Text)
|
||||
{
|
||||
height += TextFieldHeight;
|
||||
}
|
||||
else // Image
|
||||
{
|
||||
height += ImageFieldHeight;
|
||||
|
||||
// Add preview height if an image is assigned
|
||||
var imageProperty = property.FindPropertyRelative("_image");
|
||||
if (imageProperty.objectReferenceValue != null)
|
||||
{
|
||||
height += PropertySpacing + PreviewHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
// Create a property field and indent it
|
||||
var contentRect = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
|
||||
var indent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
// Get properties
|
||||
var contentTypeProperty = property.FindPropertyRelative("_contentType");
|
||||
var textProperty = property.FindPropertyRelative("_text");
|
||||
var imageProperty = property.FindPropertyRelative("_image");
|
||||
|
||||
// Calculate rects
|
||||
var typeRect = new Rect(contentRect.x, contentRect.y, contentRect.width, TypeSelectorHeight);
|
||||
var contentFieldRect = new Rect(
|
||||
contentRect.x,
|
||||
contentRect.y + TypeSelectorHeight + PropertySpacing,
|
||||
contentRect.width,
|
||||
contentTypeProperty.enumValueIndex == (int)DialogueContentType.Text ? TextFieldHeight : ImageFieldHeight);
|
||||
|
||||
// Draw the content type dropdown
|
||||
EditorGUI.PropertyField(typeRect, contentTypeProperty, GUIContent.none);
|
||||
|
||||
// Draw the appropriate field based on content type
|
||||
if (contentTypeProperty.enumValueIndex == (int)DialogueContentType.Text)
|
||||
{
|
||||
// Text field with word wrap for multi-line input
|
||||
textProperty.stringValue = EditorGUI.TextArea(contentFieldRect, textProperty.stringValue);
|
||||
}
|
||||
else // Image
|
||||
{
|
||||
// Draw the image field
|
||||
EditorGUI.PropertyField(contentFieldRect, imageProperty, GUIContent.none);
|
||||
|
||||
// Draw a preview if an image is assigned
|
||||
if (imageProperty.objectReferenceValue != null)
|
||||
{
|
||||
var sprite = imageProperty.objectReferenceValue as Sprite;
|
||||
if (sprite != null)
|
||||
{
|
||||
var previewRect = new Rect(
|
||||
contentRect.x,
|
||||
contentFieldRect.y + contentFieldRect.height + PropertySpacing,
|
||||
contentRect.width,
|
||||
PreviewHeight);
|
||||
|
||||
// Draw the preview with preserved aspect ratio
|
||||
DrawSpritePreview(previewRect, sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel = indent;
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
private void DrawSpritePreview(Rect position, Sprite sprite)
|
||||
{
|
||||
if (sprite == null || sprite.texture == null) return;
|
||||
|
||||
// Calculate aspect-preserved rect
|
||||
float aspectRatio = sprite.rect.width / sprite.rect.height;
|
||||
float targetWidth = Mathf.Min(position.width, position.height * aspectRatio);
|
||||
float targetHeight = targetWidth / aspectRatio;
|
||||
|
||||
// Center the preview
|
||||
Rect previewRect = new Rect(
|
||||
position.x + (position.width - targetWidth) * 0.5f,
|
||||
position.y + (position.height - targetHeight) * 0.5f,
|
||||
targetWidth,
|
||||
targetHeight
|
||||
);
|
||||
|
||||
// Draw the sprite preview
|
||||
EditorGUI.DrawPreviewTexture(previewRect, sprite.texture, null, ScaleMode.ScaleToFit);
|
||||
|
||||
// Draw a border around the preview
|
||||
GUI.Box(previewRect, GUIContent.none);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Editor/Dialogue/DialogueContentDrawer.cs.meta
Normal file
3
Assets/Editor/Dialogue/DialogueContentDrawer.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f77e7b681b7f464f96242172ea625ed4
|
||||
timeCreated: 1759912655
|
||||
@@ -12,6 +12,7 @@ GameObject:
|
||||
- component: {fileID: 9002038557409323574}
|
||||
- component: {fileID: 4498241824153346754}
|
||||
- component: {fileID: 3123748273643935430}
|
||||
- component: {fileID: 8484489322432759371}
|
||||
m_Layer: 5
|
||||
m_Name: SpeechBubble
|
||||
m_TagString: Untagged
|
||||
@@ -34,11 +35,11 @@ RectTransform:
|
||||
- {fileID: 1539728007164444029}
|
||||
m_Father: {fileID: 3484825090253933040}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 50}
|
||||
m_SizeDelta: {x: 400, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 1}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 600, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!114 &9002038557409323574
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -99,6 +100,123 @@ MonoBehaviour:
|
||||
m_FlexibleWidth: -1
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!114 &8484489322432759371
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1494212192306772670}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: cb3605ae81a54d2689504e0cd456ac27, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::Dialogue.SpeechBubble
|
||||
textDisplay: {fileID: 4573570654593171780}
|
||||
imageDisplay: {fileID: 4814676392695871198}
|
||||
displayMode: 1
|
||||
typewriterSpeed: 0.05
|
||||
typingSoundSource: {fileID: 0}
|
||||
typingSoundFrequency: 3
|
||||
useRichText: 1
|
||||
dialogueDisplayTime: 3
|
||||
dialoguePromptText: . . .
|
||||
--- !u!1 &3571537114331005905
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7453431659909988258}
|
||||
- component: {fileID: 7239040132725875785}
|
||||
- component: {fileID: 4814676392695871198}
|
||||
- component: {fileID: 7738447742327076413}
|
||||
m_Layer: 5
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
--- !u!224 &7453431659909988258
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3571537114331005905}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1539728007164444029}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 300, y: -125}
|
||||
m_SizeDelta: {x: 200, y: 200}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &7239040132725875785
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3571537114331005905}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &4814676392695871198
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3571537114331005905}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: -9213056636207805707, guid: 00354ded9d8f8d643acc14837a229544, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 1
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &7738447742327076413
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3571537114331005905}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: 200
|
||||
m_PreferredHeight: 200
|
||||
m_FlexibleWidth: -1
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!1 &5048280843231724144
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -164,7 +282,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Hey there buster!
|
||||
m_text: Hey there, buster!
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
@@ -191,8 +309,8 @@ MonoBehaviour:
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 35
|
||||
m_fontSizeBase: 35
|
||||
m_fontSize: 55
|
||||
m_fontSizeBase: 55
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
@@ -364,18 +482,19 @@ RectTransform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8341977934938436915}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -0.9999999}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 7704981663008171144}
|
||||
- {fileID: 7453431659909988258}
|
||||
m_Father: {fileID: 8307219291215824345}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!222 &2528298462582055986
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -433,7 +552,7 @@ MonoBehaviour:
|
||||
m_Bottom: 70
|
||||
m_ChildAlignment: 4
|
||||
m_Spacing: 0
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandWidth: 0
|
||||
m_ChildForceExpandHeight: 0
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 1
|
||||
|
||||
@@ -972,11 +972,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 2.55
|
||||
value: 0.6806664
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 3.17
|
||||
value: 1.4252888
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
@@ -1068,6 +1068,7 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::Dialogue.SpeechBubble
|
||||
textDisplay: {fileID: 677854361}
|
||||
imageDisplay: {fileID: 0}
|
||||
displayMode: 1
|
||||
typewriterSpeed: 0.02
|
||||
typingSoundSource: {fileID: 0}
|
||||
@@ -1832,6 +1833,7 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::Dialogue.SpeechBubble
|
||||
textDisplay: {fileID: 614125440}
|
||||
imageDisplay: {fileID: 0}
|
||||
displayMode: 1
|
||||
typewriterSpeed: 0.02
|
||||
typingSoundSource: {fileID: 0}
|
||||
@@ -2892,11 +2894,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 2.55
|
||||
value: 0.5560436
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 3.17
|
||||
value: 1.3006666
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
|
||||
@@ -1487,31 +1487,6 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 5145306031820616614, guid: fbbe1f4baf226904b96f839fe0c00181, type: 3}
|
||||
m_PrefabInstance: {fileID: 94815899}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &103777726 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 1494212192306772670, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
m_PrefabInstance: {fileID: 1743421791}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &103777731
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 103777726}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: cb3605ae81a54d2689504e0cd456ac27, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::Dialogue.SpeechBubble
|
||||
textDisplay: {fileID: 1103549804}
|
||||
displayMode: 1
|
||||
typewriterSpeed: 0.05
|
||||
typingSoundSource: {fileID: 0}
|
||||
typingSoundFrequency: 3
|
||||
useRichText: 1
|
||||
dialogueDisplayTime: 3
|
||||
dialoguePromptText: . . .
|
||||
--- !u!1001 &104952029
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -446788,17 +446763,6 @@ PrefabInstance:
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 21238928}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
|
||||
--- !u!114 &1103549804 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 4573570654593171780, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
m_PrefabInstance: {fileID: 1743421791}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
--- !u!1 &1106104746
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -456563,11 +456527,11 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 2.8
|
||||
value: 1.3000008
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 5.34
|
||||
value: 3.840001
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3484825090253933040, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
@@ -457644,14 +457608,6 @@ PrefabInstance:
|
||||
propertyPath: m_Camera
|
||||
value:
|
||||
objectReference: {fileID: 1653475492}
|
||||
- target: {fileID: 4573570654593171780, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_fontSize
|
||||
value: 80
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4573570654593171780, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_fontSizeBase
|
||||
value: 80
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6499933157207406972, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: DialogueCanvas
|
||||
@@ -457684,13 +457640,18 @@ PrefabInstance:
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8307219291215824345, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: -131.34683
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8307219291215824345, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: -183.88577
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 1494212192306772670, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 103777731}
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: a8b0a1c6cf21352439dc24d3b03182db, type: 3}
|
||||
--- !u!224 &1743421792 stripped
|
||||
RectTransform:
|
||||
|
||||
@@ -78,13 +78,80 @@ namespace Dialogue
|
||||
|
||||
AdvanceDialogueState();
|
||||
|
||||
// Get the current dialogue line
|
||||
// Check if we have DialogueContent available
|
||||
DialogueContent content = GetCurrentDialogueContent();
|
||||
if (content != null)
|
||||
{
|
||||
// Display the content with the new method that handles both text and images
|
||||
speechBubble.DisplayDialogueContent(content, HasAnyLines());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back to legacy text-only method
|
||||
string line = GetCurrentDialogueLine();
|
||||
|
||||
// Display the line with the new method that handles timed updates
|
||||
speechBubble.DisplayDialogueLine(line, HasAnyLines());
|
||||
}
|
||||
}
|
||||
|
||||
// Advance dialogue state for next interaction
|
||||
/// <summary>
|
||||
/// Get the current dialogue content (text or image)
|
||||
/// </summary>
|
||||
/// <returns>DialogueContent or null if only legacy text content is available</returns>
|
||||
private DialogueContent GetCurrentDialogueContent()
|
||||
{
|
||||
// Initialize if needed
|
||||
if (!initialized)
|
||||
{
|
||||
StartDialogue();
|
||||
}
|
||||
|
||||
if (!IsActive || IsCompleted || currentNode == null)
|
||||
return null;
|
||||
|
||||
// Check if we have DialogueContent available
|
||||
if (currentNode.dialogueContent != null && currentNode.dialogueContent.Count > 0)
|
||||
{
|
||||
// For WaitOnSlot nodes, use the appropriate content based on slot state
|
||||
if (currentNode.nodeType == RuntimeDialogueNodeType.WaitOnSlot)
|
||||
{
|
||||
// Choose the appropriate content collection based on the current slot state
|
||||
List<DialogueContent> contentForState = currentNode.dialogueContent; // Default content
|
||||
|
||||
switch (_currentSlotState)
|
||||
{
|
||||
case ItemSlotState.Incorrect:
|
||||
// Use incorrect item content if available
|
||||
if (currentNode.incorrectItemContent != null && currentNode.incorrectItemContent.Count > 0)
|
||||
contentForState = currentNode.incorrectItemContent;
|
||||
break;
|
||||
|
||||
case ItemSlotState.Forbidden:
|
||||
// Use forbidden item content if available
|
||||
if (currentNode.forbiddenItemContent != null && currentNode.forbiddenItemContent.Count > 0)
|
||||
contentForState = currentNode.forbiddenItemContent;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have content for this state, return the current one
|
||||
if (contentForState != null && contentForState.Count > 0)
|
||||
{
|
||||
// Make sure index is within bounds
|
||||
int index = Mathf.Clamp(currentLineIndex, 0, contentForState.Count - 1);
|
||||
return contentForState[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For other node types, use the default dialogueContent
|
||||
if (currentLineIndex >= 0 && currentLineIndex < currentNode.dialogueContent.Count)
|
||||
{
|
||||
return currentNode.dialogueContent[currentLineIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No DialogueContent available, will fall back to legacy text handling
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
@@ -560,46 +627,94 @@ namespace Dialogue
|
||||
// Special case: if condition has been satisfied but not yet advanced, we should show lines
|
||||
if (_conditionSatisfiedPendingAdvance && !string.IsNullOrEmpty(currentNode.nextNodeID))
|
||||
{
|
||||
// Check if the next node would have lines
|
||||
// Check if the next node would have lines or content
|
||||
RuntimeDialogueNode nextNode = dialogueGraph.GetNodeByID(currentNode.nextNodeID);
|
||||
return nextNode != null && (nextNode.dialogueLines.Count > 0 || nextNode.nodeType != RuntimeDialogueNodeType.End);
|
||||
return nextNode != null &&
|
||||
((nextNode.dialogueLines != null && nextNode.dialogueLines.Count > 0) ||
|
||||
(nextNode.dialogueContent != null && nextNode.dialogueContent.Count > 0) ||
|
||||
nextNode.nodeType != RuntimeDialogueNodeType.End);
|
||||
}
|
||||
|
||||
// For WaitOnSlot nodes, check for lines based on current slot state
|
||||
// For WaitOnSlot nodes, check for lines or content based on current slot state
|
||||
if (currentNode.nodeType == RuntimeDialogueNodeType.WaitOnSlot)
|
||||
{
|
||||
// Choose the appropriate line collection based on the current slot state
|
||||
List<string> linesForState = currentNode.dialogueLines; // Default lines
|
||||
// First check for DialogueContent
|
||||
if (currentNode.dialogueContent != null && currentNode.dialogueContent.Count > 0)
|
||||
{
|
||||
// Choose the appropriate content collection based on the current slot state
|
||||
List<DialogueContent> contentForState = currentNode.dialogueContent;
|
||||
|
||||
switch (_currentSlotState)
|
||||
{
|
||||
case ItemSlotState.Incorrect:
|
||||
if (currentNode.incorrectItemContent != null && currentNode.incorrectItemContent.Count > 0)
|
||||
contentForState = currentNode.incorrectItemContent;
|
||||
break;
|
||||
|
||||
case ItemSlotState.Forbidden:
|
||||
if (currentNode.forbiddenItemContent != null && currentNode.forbiddenItemContent.Count > 0)
|
||||
contentForState = currentNode.forbiddenItemContent;
|
||||
break;
|
||||
}
|
||||
|
||||
if (contentForState.Count > 0)
|
||||
{
|
||||
if (currentLineIndex < contentForState.Count - 1 || currentNode.loopThroughLines)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to legacy text lines
|
||||
List<string> linesForState = currentNode.dialogueLines;
|
||||
|
||||
switch (_currentSlotState)
|
||||
{
|
||||
case ItemSlotState.Incorrect:
|
||||
// Use incorrect item lines if available, otherwise fall back to default lines
|
||||
if (currentNode.incorrectItemLines != null && currentNode.incorrectItemLines.Count > 0)
|
||||
linesForState = currentNode.incorrectItemLines;
|
||||
break;
|
||||
|
||||
case ItemSlotState.Forbidden:
|
||||
// Use forbidden item lines if available, otherwise fall back to default lines
|
||||
if (currentNode.forbiddenItemLines != null && currentNode.forbiddenItemLines.Count > 0)
|
||||
linesForState = currentNode.forbiddenItemLines;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if we have any lines for the current state
|
||||
if (linesForState != null && linesForState.Count > 0)
|
||||
{
|
||||
// If we're not at the end of the lines or we loop through them
|
||||
if (currentLineIndex < linesForState.Count - 1 || currentNode.loopThroughLines)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// For other node types, use the standard check
|
||||
else if (currentNode.dialogueLines.Count > 0)
|
||||
// For other node types, check for DialogueContent first, then fall back to legacy text
|
||||
else
|
||||
{
|
||||
// Check for DialogueContent
|
||||
if (currentNode.dialogueContent != null && currentNode.dialogueContent.Count > 0)
|
||||
{
|
||||
if (currentLineIndex < currentNode.dialogueContent.Count - 1 || currentNode.loopThroughLines)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're at the end of content but not waiting for a condition and have a next node
|
||||
if (!IsWaitingForCondition() && !string.IsNullOrEmpty(currentNode.nextNodeID))
|
||||
{
|
||||
RuntimeDialogueNode nextNode = dialogueGraph.GetNodeByID(currentNode.nextNodeID);
|
||||
return nextNode != null &&
|
||||
((nextNode.dialogueContent != null && nextNode.dialogueContent.Count > 0) ||
|
||||
(nextNode.dialogueLines != null && nextNode.dialogueLines.Count > 0) ||
|
||||
nextNode.nodeType != RuntimeDialogueNodeType.End);
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to legacy text lines
|
||||
if (currentNode.dialogueLines != null && currentNode.dialogueLines.Count > 0)
|
||||
{
|
||||
// If we're not at the end of the lines or we loop through them
|
||||
if (currentLineIndex < currentNode.dialogueLines.Count - 1 || currentNode.loopThroughLines)
|
||||
{
|
||||
return true;
|
||||
@@ -608,12 +723,14 @@ namespace Dialogue
|
||||
// If we're at the end of lines but not waiting for a condition and have a next node
|
||||
if (!IsWaitingForCondition() && !string.IsNullOrEmpty(currentNode.nextNodeID))
|
||||
{
|
||||
// We need to check if the next node would have lines
|
||||
RuntimeDialogueNode nextNode = dialogueGraph.GetNodeByID(currentNode.nextNodeID);
|
||||
return nextNode != null && (nextNode.dialogueLines.Count > 0 || nextNode.nodeType != RuntimeDialogueNodeType.End);
|
||||
return nextNode != null &&
|
||||
((nextNode.dialogueContent != null && nextNode.dialogueContent.Count > 0) ||
|
||||
(nextNode.dialogueLines != null && nextNode.dialogueLines.Count > 0) ||
|
||||
nextNode.nodeType != RuntimeDialogueNodeType.End);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
76
Assets/Scripts/Dialogue/DialogueContent.cs
Normal file
76
Assets/Scripts/Dialogue/DialogueContent.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// Content type for dialogue entries
|
||||
/// </summary>
|
||||
public enum DialogueContentType
|
||||
{
|
||||
Text,
|
||||
Image
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper class for dialogue content that can be either text or image
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class DialogueContent
|
||||
{
|
||||
[SerializeField] private DialogueContentType _contentType = DialogueContentType.Text;
|
||||
[SerializeField] private string _text = string.Empty;
|
||||
[SerializeField] private Sprite _image = null;
|
||||
|
||||
/// <summary>
|
||||
/// The type of content this entry contains
|
||||
/// </summary>
|
||||
public DialogueContentType ContentType => _contentType;
|
||||
|
||||
/// <summary>
|
||||
/// The text content (valid when ContentType is Text)
|
||||
/// </summary>
|
||||
public string Text => _text;
|
||||
|
||||
/// <summary>
|
||||
/// The image content (valid when ContentType is Image)
|
||||
/// </summary>
|
||||
public Sprite Image => _image;
|
||||
|
||||
/// <summary>
|
||||
/// Create text content
|
||||
/// </summary>
|
||||
/// <param name="text">The text to display</param>
|
||||
public static DialogueContent CreateText(string text)
|
||||
{
|
||||
return new DialogueContent
|
||||
{
|
||||
_contentType = DialogueContentType.Text,
|
||||
_text = text
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create image content
|
||||
/// </summary>
|
||||
/// <param name="image">The image to display</param>
|
||||
public static DialogueContent CreateImage(Sprite image)
|
||||
{
|
||||
return new DialogueContent
|
||||
{
|
||||
_contentType = DialogueContentType.Image,
|
||||
_image = image
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this content
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return ContentType == DialogueContentType.Text
|
||||
? $"Text: {_text}"
|
||||
: $"Image: {_image?.name ?? "None"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Dialogue/DialogueContent.cs.meta
Normal file
3
Assets/Scripts/Dialogue/DialogueContent.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b479dc736d44dea83d4d2cf4d940d8b
|
||||
timeCreated: 1759912630
|
||||
@@ -36,10 +36,14 @@ namespace Dialogue
|
||||
public RuntimeDialogueNodeType nodeType;
|
||||
public string nextNodeID;
|
||||
|
||||
// Basic dialogue
|
||||
// Basic dialogue - legacy text-only field
|
||||
[HideInInspector]
|
||||
public List<string> dialogueLines = new List<string>();
|
||||
public bool loopThroughLines;
|
||||
|
||||
// New mixed content field that supports both text and images
|
||||
public List<DialogueContent> dialogueContent = new List<DialogueContent>();
|
||||
|
||||
// Conditional nodes
|
||||
public string puzzleStepID; // For WaitOnPuzzleStep
|
||||
public string pickupItemID; // For WaitOnPickup
|
||||
@@ -47,9 +51,14 @@ namespace Dialogue
|
||||
public string combinationResultItemID; // For WaitOnCombination
|
||||
|
||||
// For WaitOnSlot - different responses
|
||||
[HideInInspector]
|
||||
public List<string> incorrectItemLines = new List<string>();
|
||||
public bool loopThroughIncorrectLines;
|
||||
public List<DialogueContent> incorrectItemContent = new List<DialogueContent>();
|
||||
|
||||
[HideInInspector]
|
||||
public List<string> forbiddenItemLines = new List<string>();
|
||||
public bool loopThroughForbiddenLines;
|
||||
public List<DialogueContent> forbiddenItemContent = new List<DialogueContent>();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Dialogue
|
||||
{
|
||||
@@ -18,6 +19,7 @@ namespace Dialogue
|
||||
public class SpeechBubble : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TextMeshProUGUI textDisplay;
|
||||
[SerializeField] private Image imageDisplay; // New field for displaying images
|
||||
[SerializeField] private TextDisplayMode displayMode = TextDisplayMode.Typewriter;
|
||||
[SerializeField] private float typewriterSpeed = 0.05f; // Time between characters in seconds
|
||||
[SerializeField] private AudioSource typingSoundSource;
|
||||
@@ -29,11 +31,18 @@ namespace Dialogue
|
||||
private Coroutine typewriterCoroutine;
|
||||
private Coroutine promptUpdateCoroutine;
|
||||
private string currentFullText = string.Empty;
|
||||
private Sprite currentImage = null;
|
||||
private bool isVisible = false;
|
||||
private DialogueContentType currentContentType = DialogueContentType.Text;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Ensure we have both components
|
||||
if (textDisplay == null)
|
||||
Debug.LogError("SpeechBubble: TextMeshProUGUI component is not assigned!");
|
||||
|
||||
if (imageDisplay == null)
|
||||
Debug.LogError("SpeechBubble: Image component is not assigned!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,6 +101,7 @@ namespace Dialogue
|
||||
}
|
||||
|
||||
currentFullText = text;
|
||||
currentContentType = DialogueContentType.Text;
|
||||
|
||||
// Stop any existing typewriter effect
|
||||
if (typewriterCoroutine != null)
|
||||
@@ -100,6 +110,13 @@ namespace Dialogue
|
||||
typewriterCoroutine = null;
|
||||
}
|
||||
|
||||
// Activate text display, deactivate image display
|
||||
textDisplay.gameObject.SetActive(true);
|
||||
if (imageDisplay != null)
|
||||
{
|
||||
imageDisplay.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Display text based on the selected mode
|
||||
if (displayMode == TextDisplayMode.Instant)
|
||||
{
|
||||
@@ -259,5 +276,117 @@ namespace Dialogue
|
||||
|
||||
typewriterCoroutine = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the image to display in the speech bubble
|
||||
/// </summary>
|
||||
/// <param name="sprite">Sprite to display</param>
|
||||
public void SetImage(Sprite sprite)
|
||||
{
|
||||
if (imageDisplay == null)
|
||||
{
|
||||
Debug.LogError("SpeechBubble: Image component is not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
currentImage = sprite;
|
||||
currentContentType = DialogueContentType.Image;
|
||||
|
||||
// Activate image display, set the sprite
|
||||
imageDisplay.gameObject.SetActive(true);
|
||||
imageDisplay.sprite = sprite;
|
||||
|
||||
// Deactivate text display
|
||||
if (textDisplay != null)
|
||||
{
|
||||
textDisplay.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Make sure the bubble is visible when setting image
|
||||
if (!isVisible)
|
||||
Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the displayed image
|
||||
/// </summary>
|
||||
public void ClearImage()
|
||||
{
|
||||
SetImage(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the content of the speech bubble (text or image)
|
||||
/// </summary>
|
||||
/// <param name="text">Text content</param>
|
||||
/// <param name="image">Image content</param>
|
||||
public void SetContent(string text, Sprite image)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
currentContentType = DialogueContentType.Text;
|
||||
SetText(text);
|
||||
}
|
||||
else if (image != null)
|
||||
{
|
||||
currentContentType = DialogueContentType.Image;
|
||||
SetImage(image);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current content type of the speech bubble
|
||||
/// </summary>
|
||||
/// <returns>Current content type</returns>
|
||||
public DialogueContentType GetCurrentContentType()
|
||||
{
|
||||
return currentContentType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display dialogue content (text or image)
|
||||
/// </summary>
|
||||
/// <param name="content">The dialogue content to display</param>
|
||||
/// <param name="hasMoreDialogue">Whether there are more dialogue content items available</param>
|
||||
public void DisplayDialogueContent(DialogueContent content, bool hasMoreDialogue)
|
||||
{
|
||||
// Cancel any existing prompt update
|
||||
if (promptUpdateCoroutine != null)
|
||||
{
|
||||
StopCoroutine(promptUpdateCoroutine);
|
||||
promptUpdateCoroutine = null;
|
||||
}
|
||||
|
||||
if (content == null)
|
||||
{
|
||||
UpdatePromptVisibility(hasMoreDialogue);
|
||||
return;
|
||||
}
|
||||
|
||||
// Display the content based on its type
|
||||
currentContentType = content.ContentType;
|
||||
|
||||
if (content.ContentType == DialogueContentType.Text)
|
||||
{
|
||||
// Show text display, hide image display
|
||||
textDisplay.gameObject.SetActive(true);
|
||||
if (imageDisplay != null) imageDisplay.gameObject.SetActive(false);
|
||||
|
||||
// Display the text
|
||||
DisplayDialogueLine(content.Text, hasMoreDialogue);
|
||||
}
|
||||
else // Image content
|
||||
{
|
||||
// Hide text display, show image display
|
||||
textDisplay.gameObject.SetActive(false);
|
||||
if (imageDisplay != null) imageDisplay.gameObject.SetActive(true);
|
||||
|
||||
// Set the image
|
||||
SetImage(content.Image);
|
||||
|
||||
// After a delay, update the prompt visibility
|
||||
promptUpdateCoroutine = StartCoroutine(UpdatePromptAfterDelay(hasMoreDialogue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 877344c7a0014922bc3a2a469e03792d
|
||||
timeCreated: 1759050622
|
||||
Reference in New Issue
Block a user