added code from unity
This commit is contained in:
17
Runtime/GameLogSaveData.cs
Normal file
17
Runtime/GameLogSaveData.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Jovian.InGameLogging {
|
||||
[Serializable]
|
||||
public sealed class GameLogSaveData {
|
||||
public List<LogEntry> entries;
|
||||
|
||||
public GameLogSaveData() {
|
||||
entries = new List<LogEntry>();
|
||||
}
|
||||
|
||||
public GameLogSaveData(List<LogEntry> entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/GameLogSaveData.cs.meta
Normal file
2
Runtime/GameLogSaveData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 965eab6edce9dbc49b93d0bda0ad6f6c
|
||||
122
Runtime/GameLogStore.cs
Normal file
122
Runtime/GameLogStore.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Jovian.InGameLogging {
|
||||
public sealed class GameLogStore : IGameLogStore {
|
||||
private readonly LogEntry[] buffer;
|
||||
private readonly HashSet<LogChannel> disabledChannels = new();
|
||||
private int head;
|
||||
private int count;
|
||||
|
||||
public int Count => count;
|
||||
public int Capacity => buffer.Length;
|
||||
|
||||
public event Action<LogEntry> OnEntryAdded;
|
||||
public event Action OnCleared;
|
||||
|
||||
public GameLogStore(int capacity = 500) {
|
||||
buffer = new LogEntry[capacity];
|
||||
head = 0;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
public void Add(LogChannel channel, string message) {
|
||||
if(disabledChannels.Contains(channel)) {
|
||||
return;
|
||||
}
|
||||
var entry = new LogEntry(message, channel, Time.time);
|
||||
buffer[head] = entry;
|
||||
head = (head + 1) % buffer.Length;
|
||||
if(count < buffer.Length) {
|
||||
count++;
|
||||
}
|
||||
OnEntryAdded?.Invoke(entry);
|
||||
}
|
||||
|
||||
public void EnableChannel(LogChannel channel) {
|
||||
disabledChannels.Remove(channel);
|
||||
}
|
||||
|
||||
public void DisableChannel(LogChannel channel) {
|
||||
disabledChannels.Add(channel);
|
||||
}
|
||||
|
||||
public bool IsChannelEnabled(LogChannel channel) {
|
||||
return !disabledChannels.Contains(channel);
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
head = 0;
|
||||
count = 0;
|
||||
OnCleared?.Invoke();
|
||||
}
|
||||
|
||||
public void Clear(LogChannel channel) {
|
||||
var kept = new List<LogEntry>(count);
|
||||
var entries = GetEntries();
|
||||
for(var i = 0; i < entries.Length; i++) {
|
||||
if(entries[i].channel != channel) {
|
||||
kept.Add(entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
head = 0;
|
||||
count = 0;
|
||||
for(var i = 0; i < kept.Count; i++) {
|
||||
buffer[i] = kept[i];
|
||||
count++;
|
||||
}
|
||||
head = count % buffer.Length;
|
||||
OnCleared?.Invoke();
|
||||
}
|
||||
|
||||
public ReadOnlySpan<LogEntry> GetEntries() {
|
||||
if(count < buffer.Length) {
|
||||
return new ReadOnlySpan<LogEntry>(buffer, 0, count);
|
||||
}
|
||||
var result = new LogEntry[count];
|
||||
var start = head;
|
||||
for(var i = 0; i < count; i++) {
|
||||
result[i] = buffer[(start + i) % buffer.Length];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetEntries(LogChannel channel, List<LogEntry> results) {
|
||||
results.Clear();
|
||||
var entries = GetEntries();
|
||||
for(var i = 0; i < entries.Length; i++) {
|
||||
if(entries[i].channel == channel) {
|
||||
results.Add(entries[i]);
|
||||
}
|
||||
}
|
||||
return results.Count;
|
||||
}
|
||||
|
||||
public GameLogSaveData GetSaveData() {
|
||||
var entries = GetEntries();
|
||||
var list = new List<LogEntry>(entries.Length);
|
||||
foreach(var t in entries) {
|
||||
list.Add(t);
|
||||
}
|
||||
return new GameLogSaveData(list);
|
||||
}
|
||||
|
||||
public void RestoreFromSaveData(GameLogSaveData data) {
|
||||
head = 0;
|
||||
count = 0;
|
||||
if(data?.entries == null) {
|
||||
OnCleared?.Invoke();
|
||||
return;
|
||||
}
|
||||
var startIndex = Math.Max(0, data.entries.Count - buffer.Length);
|
||||
for(var i = startIndex; i < data.entries.Count; i++) {
|
||||
buffer[count] = data.entries[i];
|
||||
count++;
|
||||
}
|
||||
head = count % buffer.Length;
|
||||
OnCleared?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/GameLogStore.cs.meta
Normal file
2
Runtime/GameLogStore.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1eb9ff03ddd225e46852cb92ba213bc7
|
||||
25
Runtime/IGameLogStore.cs
Normal file
25
Runtime/IGameLogStore.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Jovian.InGameLogging {
|
||||
public interface IGameLogStore {
|
||||
int Count { get; }
|
||||
int Capacity { get; }
|
||||
|
||||
void Add(LogChannel channel, string message);
|
||||
void EnableChannel(LogChannel channel);
|
||||
void DisableChannel(LogChannel channel);
|
||||
bool IsChannelEnabled(LogChannel channel);
|
||||
void Clear();
|
||||
void Clear(LogChannel channel);
|
||||
|
||||
ReadOnlySpan<LogEntry> GetEntries();
|
||||
int GetEntries(LogChannel channel, List<LogEntry> results);
|
||||
|
||||
event Action<LogEntry> OnEntryAdded;
|
||||
event Action OnCleared;
|
||||
|
||||
GameLogSaveData GetSaveData();
|
||||
void RestoreFromSaveData(GameLogSaveData data);
|
||||
}
|
||||
}
|
||||
2
Runtime/IGameLogStore.cs.meta
Normal file
2
Runtime/IGameLogStore.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5fac89eac17e874e888928c9618e812
|
||||
36
Runtime/InGameLogger.cs
Normal file
36
Runtime/InGameLogger.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Jovian.InGameLogging {
|
||||
public readonly struct InGameLogger {
|
||||
private readonly IGameLogStore store;
|
||||
private readonly LogChannel channel;
|
||||
|
||||
public InGameLogger(IGameLogStore store, LogChannel channel) {
|
||||
this.store = store;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Log(string message) {
|
||||
store.Add(channel, message);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Log(string message, string hexColor) {
|
||||
var prefix = hexColor.Length > 0 && hexColor[0] == '#' ? "" : "#";
|
||||
store.Add(channel, $"<color={prefix}{hexColor}>{message}</color>");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Enable() {
|
||||
store.EnableChannel(channel);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Disable() {
|
||||
store.DisableChannel(channel);
|
||||
}
|
||||
|
||||
public bool IsEnabled => store.IsChannelEnabled(channel);
|
||||
}
|
||||
}
|
||||
2
Runtime/InGameLogger.cs.meta
Normal file
2
Runtime/InGameLogger.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79d11c151d20d2c41a4ad5e288a4f16f
|
||||
19
Runtime/Jovian.InGameLogging.asmdef
Normal file
19
Runtime/Jovian.InGameLogging.asmdef
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Jovian.InGameLogging",
|
||||
"rootNamespace": "Jovian.InGameLogging",
|
||||
"references": [
|
||||
"Unity.TextMeshPro",
|
||||
"Jovian.SaveSystem"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"Newtonsoft.Json.dll"
|
||||
],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
7
Runtime/Jovian.InGameLogging.asmdef.meta
Normal file
7
Runtime/Jovian.InGameLogging.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6053f37e557f955418ef96fdf46f7d6b
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
44
Runtime/LogChannel.cs
Normal file
44
Runtime/LogChannel.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Jovian.InGameLogging {
|
||||
[Serializable]
|
||||
public readonly struct LogChannel : IEquatable<LogChannel> {
|
||||
private readonly string id;
|
||||
|
||||
public string Id => id;
|
||||
|
||||
public LogChannel(string id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static readonly LogChannel Combat = new("Combat");
|
||||
public static readonly LogChannel CharacterCreation = new("CharacterCreation");
|
||||
public static readonly LogChannel World = new("World");
|
||||
public static readonly LogChannel General = new("General");
|
||||
|
||||
public bool Equals(LogChannel other) {
|
||||
return string.Equals(id, other.id, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return obj is LogChannel other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return id != null ? id.GetHashCode() : 0;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return id ?? string.Empty;
|
||||
}
|
||||
|
||||
public static bool operator ==(LogChannel left, LogChannel right) {
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(LogChannel left, LogChannel right) {
|
||||
return !left.Equals(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/LogChannel.cs.meta
Normal file
2
Runtime/LogChannel.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08b1132b10325ce4d922fa7b207db4e0
|
||||
15
Runtime/LogChannelJsonConverter.cs
Normal file
15
Runtime/LogChannelJsonConverter.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Jovian.InGameLogging {
|
||||
public sealed class LogChannelJsonConverter : JsonConverter<LogChannel> {
|
||||
public override void WriteJson(JsonWriter writer, LogChannel value, JsonSerializer serializer) {
|
||||
writer.WriteValue(value.Id);
|
||||
}
|
||||
|
||||
public override LogChannel ReadJson(JsonReader reader, Type objectType, LogChannel existingValue, bool hasExistingValue, JsonSerializer serializer) {
|
||||
var id = reader.Value as string;
|
||||
return new LogChannel(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/LogChannelJsonConverter.cs.meta
Normal file
2
Runtime/LogChannelJsonConverter.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c3eb6d23e195e345a3546749ca4e96f
|
||||
20
Runtime/LogEntry.cs
Normal file
20
Runtime/LogEntry.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Jovian.InGameLogging {
|
||||
[Serializable]
|
||||
public readonly struct LogEntry {
|
||||
[JsonProperty] public readonly string message;
|
||||
|
||||
[JsonProperty] [JsonConverter(typeof(LogChannelJsonConverter))]
|
||||
public readonly LogChannel channel;
|
||||
|
||||
[JsonProperty] public readonly float gameTime;
|
||||
|
||||
public LogEntry(string message, LogChannel channel, float gameTime) {
|
||||
this.message = message;
|
||||
this.channel = channel;
|
||||
this.gameTime = gameTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/LogEntry.cs.meta
Normal file
2
Runtime/LogEntry.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f79543f908769543ac8fc14e138df62
|
||||
8
Runtime/UI.meta
Normal file
8
Runtime/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1125aedc37d43948aeef186bccbeac8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
137
Runtime/UI/GameLogView.cs
Normal file
137
Runtime/UI/GameLogView.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Jovian.InGameLogging.UI {
|
||||
public class GameLogView : MonoBehaviour {
|
||||
[SerializeField] ScrollRect scrollRect;
|
||||
[SerializeField] RectTransform content;
|
||||
[SerializeField] LogEntryView entryPrefab;
|
||||
[SerializeField] int poolSize = 20;
|
||||
|
||||
IGameLogStore store;
|
||||
LogChannel? channelFilter;
|
||||
bool autoScroll = true;
|
||||
bool scrollingToBottom;
|
||||
Coroutine scrollCoroutine;
|
||||
|
||||
readonly List<LogEntryView> activeEntries = new();
|
||||
readonly Stack<LogEntryView> pool = new();
|
||||
|
||||
public void Initialize(IGameLogStore store, LogChannel? channelFilter = null) {
|
||||
this.store = store;
|
||||
this.channelFilter = channelFilter;
|
||||
|
||||
WarmPool();
|
||||
store.OnEntryAdded += HandleEntryAdded;
|
||||
store.OnCleared += HandleCleared;
|
||||
scrollRect.onValueChanged.AddListener(HandleScrollChanged);
|
||||
|
||||
RebuildFromStore();
|
||||
}
|
||||
|
||||
void OnDestroy() {
|
||||
if(store != null) {
|
||||
store.OnEntryAdded -= HandleEntryAdded;
|
||||
store.OnCleared -= HandleCleared;
|
||||
}
|
||||
if(scrollRect != null) {
|
||||
scrollRect.onValueChanged.RemoveListener(HandleScrollChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void WarmPool() {
|
||||
for(int i = 0; i < poolSize; i++) {
|
||||
var entry = Instantiate(entryPrefab, content);
|
||||
entry.gameObject.SetActive(false);
|
||||
pool.Push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
LogEntryView GetFromPool() {
|
||||
LogEntryView entry;
|
||||
if(pool.Count > 0) {
|
||||
entry = pool.Pop();
|
||||
}
|
||||
else {
|
||||
entry = activeEntries[0];
|
||||
activeEntries.RemoveAt(0);
|
||||
}
|
||||
entry.gameObject.SetActive(true);
|
||||
entry.transform.SetAsLastSibling();
|
||||
return entry;
|
||||
}
|
||||
|
||||
void ReturnToPool(LogEntryView entry) {
|
||||
entry.ClearEntry();
|
||||
entry.gameObject.SetActive(false);
|
||||
pool.Push(entry);
|
||||
}
|
||||
|
||||
void HandleEntryAdded(LogEntry entry) {
|
||||
if(channelFilter.HasValue && entry.channel != channelFilter.Value) {
|
||||
return;
|
||||
}
|
||||
|
||||
var view = GetFromPool();
|
||||
view.SetEntry(in entry);
|
||||
activeEntries.Add(view);
|
||||
|
||||
if(autoScroll) {
|
||||
RequestScrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestScrollToBottom() {
|
||||
if(scrollCoroutine != null) {
|
||||
StopCoroutine(scrollCoroutine);
|
||||
}
|
||||
scrollCoroutine = StartCoroutine(ScrollToBottomRoutine());
|
||||
}
|
||||
|
||||
IEnumerator ScrollToBottomRoutine() {
|
||||
scrollingToBottom = true;
|
||||
yield return null;
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(content);
|
||||
scrollRect.verticalNormalizedPosition = 0f;
|
||||
yield return null;
|
||||
scrollingToBottom = false;
|
||||
scrollCoroutine = null;
|
||||
}
|
||||
|
||||
void HandleCleared() {
|
||||
for(int i = activeEntries.Count - 1; i >= 0; i--) {
|
||||
ReturnToPool(activeEntries[i]);
|
||||
}
|
||||
activeEntries.Clear();
|
||||
|
||||
if(store.Count > 0) {
|
||||
RebuildFromStore();
|
||||
}
|
||||
}
|
||||
|
||||
void RebuildFromStore() {
|
||||
var entries = store.GetEntries();
|
||||
for(int i = 0; i < entries.Length; i++) {
|
||||
if(channelFilter.HasValue && entries[i].channel != channelFilter.Value) {
|
||||
continue;
|
||||
}
|
||||
var view = GetFromPool();
|
||||
view.SetEntry(in entries[i]);
|
||||
activeEntries.Add(view);
|
||||
}
|
||||
|
||||
if(autoScroll) {
|
||||
RequestScrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleScrollChanged(Vector2 position) {
|
||||
if(scrollingToBottom) {
|
||||
return;
|
||||
}
|
||||
autoScroll = position.y <= 0.01f;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/UI/GameLogView.cs.meta
Normal file
2
Runtime/UI/GameLogView.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 786fb23f28122964cb30678ea785bd40
|
||||
16
Runtime/UI/LogEntryView.cs
Normal file
16
Runtime/UI/LogEntryView.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Jovian.InGameLogging.UI {
|
||||
public class LogEntryView : MonoBehaviour {
|
||||
[SerializeField] TMP_Text messageText;
|
||||
|
||||
public void SetEntry(in LogEntry entry) {
|
||||
messageText.text = entry.message;
|
||||
}
|
||||
|
||||
public void ClearEntry() {
|
||||
messageText.text = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/UI/LogEntryView.cs.meta
Normal file
2
Runtime/UI/LogEntryView.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5526887cdf77a54439357be8b5754ffc
|
||||
Reference in New Issue
Block a user