diff --git a/CLEditor/windows/PropertyBox.xaml.cs b/CLEditor/windows/PropertyBox.xaml.cs index f2c1f3a0b45c4ca222ab3bf1098aff55bafbbac1..41ca23db1d33bc69c2fb8b5051ed2a8d832204eb 100644 --- a/CLEditor/windows/PropertyBox.xaml.cs +++ b/CLEditor/windows/PropertyBox.xaml.cs @@ -1,5 +1,4 @@ -using System; -using System.Windows; +using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; @@ -11,23 +10,25 @@ namespace CLEngine.Editor.windows /// /// PropertyBox.xaml 的交互逻辑 /// - public partial class PropertyBox : UserControl + public partial class PropertyBox { - object selected; + private object selected; public object SelectedObject { get { return PropertyGrid.SelectedObject; } set { - Dispatcher.Invoke(delegate - { - selected = value; - PropertyGrid.SelectedObject = value; - Title.Content = CHelper.SplitCamelCase(value.ToString()); - - SettingsBtn.Visibility = value is ObjectComponent ? System.Windows.Visibility.Visible : System.Windows.Visibility.Hidden; - }); + Dispatcher?.Invoke(delegate + { + selected = value; + PropertyGrid.SelectedObject = value; + Title.Content = CHelper.SplitCamelCase(value.ToString()); + + SettingsBtn.Visibility = value is ObjectComponent + ? Visibility.Visible + : Visibility.Hidden; + }); } } @@ -36,16 +37,6 @@ namespace CLEngine.Editor.windows InitializeComponent(); } - void PropertyGrid_SelectedPropertyItemChanged(object sender, RoutedPropertyChangedEventArgs e) - { - - } - - void PropertyGrid_SelectedObjectChanged(object sender, RoutedPropertyChangedEventArgs e) - { - - } - private void VisibilityHandlerBtn_MouseUp(object sender, MouseButtonEventArgs e) { ToggleExpand(); @@ -58,39 +49,36 @@ namespace CLEngine.Editor.windows //{ bool expanded = false; - if (PropertyGridContainer.Visibility == System.Windows.Visibility.Collapsed) + switch (PropertyGridContainer.Visibility) { - PropertyGridContainer.Visibility = System.Windows.Visibility.Visible; - VisibilityHandlerBtn.Source = (ImageSource)new ImageSourceConverter().ConvertFrom("content/_arrow_down.png"); - expanded = true; - } - else if (PropertyGridContainer.Visibility == System.Windows.Visibility.Visible) - { - PropertyGridContainer.Visibility = System.Windows.Visibility.Collapsed; - VisibilityHandlerBtn.Source = (ImageSource)new ImageSourceConverter().ConvertFrom("content/_arrow_right.png"); + case Visibility.Collapsed: + PropertyGridContainer.Visibility = Visibility.Visible; + VisibilityHandlerBtn.Source = (ImageSource)new ImageSourceConverter().ConvertFrom("content/_arrow_down.png"); + expanded = true; + break; + case Visibility.Visible: + PropertyGridContainer.Visibility = Visibility.Collapsed; + VisibilityHandlerBtn.Source = (ImageSource)new ImageSourceConverter().ConvertFrom("content/_arrow_right.png"); + break; } - if (SelectedObject is ObjectComponent) - (SelectedObject as ObjectComponent).EditorExpanded = expanded; - //})); + if (SelectedObject is ObjectComponent component) + component.EditorExpanded = expanded; } private void PropertyGrid_MouseEnter(object sender, MouseEventArgs e) { - Dispatcher.Invoke((Action)(() => - { - PropertyGrid.Update(); - })); + Dispatcher?.Invoke(() => { PropertyGrid.Update(); }); } private void SettingsBtn_MouseUp(object sender, MouseButtonEventArgs e) { if (MessageBox.Show("您确定要删除此组件吗?", "警告!", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) { - ObjectComponent oc = this.PropertyGrid.SelectedObject as ObjectComponent; - oc.Transform.GameObject.RemoveComponent(oc); + var oc = PropertyGrid.SelectedObject as ObjectComponent; + oc?.Transform.GameObject.RemoveComponent(oc); - this.Visibility = System.Windows.Visibility.Collapsed; + Visibility = Visibility.Collapsed; EditorCommands.CheckPropertyGridConsistency(); } @@ -100,8 +88,8 @@ namespace CLEngine.Editor.windows { if (e.Key == Key.Enter) { - string text = (sender as TextBox).Text.Trim(); - if (!text.Equals(string.Empty) && text.StartsWith("+")) + string text = (sender as TextBox)?.Text.Trim(); + if (text != null && (!text.Equals(string.Empty) && text.StartsWith("+"))) { text = text.Remove(0, 1); @@ -113,7 +101,7 @@ namespace CLEngine.Editor.windows SceneManager.ActiveScene.CommonTags.Insert(0, text); - (sender as TextBox).Text = text; + ((TextBox) sender).Text = text; } } } diff --git a/Engine/CLEngine.Core/CLEngine.Core.csproj b/Engine/CLEngine.Core/CLEngine.Core.csproj index 867f2e7bd914a0b4e65307a6e7e72aea4f6d461f..a4033b715a87b1ae629233bdcec61ac791601ea9 100644 --- a/Engine/CLEngine.Core/CLEngine.Core.csproj +++ b/Engine/CLEngine.Core/CLEngine.Core.csproj @@ -99,6 +99,8 @@ + + @@ -116,6 +118,7 @@ + diff --git a/Engine/CLEngine.Core/Camera.cs b/Engine/CLEngine.Core/Camera.cs index df33abd2f0cd8ff35b1ead252577b19ca6e3bcc3..a75d9a96dfe08096cd228996f495a02dd4843482 100644 --- a/Engine/CLEngine.Core/Camera.cs +++ b/Engine/CLEngine.Core/Camera.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using Microsoft.Xna.Framework; using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; @@ -14,6 +15,8 @@ namespace CLEngine.Core [Serializable, TypeConverter(typeof(ExpandableObjectConverter))] #endif [DataContract] + [SuppressMessage("ReSharper", "UnusedMember.Local")] + [SuppressMessage("ReSharper", "UnusedMember.Global")] public class Camera : SystemObject { private Matrix transformMatrix; @@ -57,7 +60,7 @@ namespace CLEngine.Core } [DataMember] - private float rotation = 0; + private float rotation; /// /// 相机的缩放倍数 @@ -95,8 +98,8 @@ namespace CLEngine.Core public Camera() { //this.transform.GameObject = this; - this.Position = Vector2.Zero; - this.zoom = 1.0f; + Position = Vector2.Zero; + zoom = 1.0f; } /// @@ -119,6 +122,7 @@ namespace CLEngine.Core /// 计算相机的变换矩阵 /// /// + [SuppressMessage("ReSharper", "PossibleLossOfFraction")] private Matrix CalculateTransform() { Vector3 scalingFactor = Vector3.One; @@ -133,18 +137,17 @@ namespace CLEngine.Core SceneManager.GameProject.Settings.ScreenHeight); // 确保我们保持真实的比例(用户输入) - float widthScale = (float)SceneManager.GraphicsDevice.Viewport.Width / width; - float heightScale = (float)SceneManager.GraphicsDevice.Viewport.Height / height; + float widthScale = SceneManager.GraphicsDevice.Viewport.Width / width; + float heightScale = SceneManager.GraphicsDevice.Viewport.Height / height; scalingFactor = new Vector3(widthScale, heightScale, 1); } Vector2 target = Position; - float aspectRatio = ((float)SceneManager.GraphicsDevice.Viewport.Width) / ((float)SceneManager.GraphicsDevice.Viewport.Height); Matrix result = Matrix.CreateTranslation(-(float)Math.Round(target.X, 1), -(float)Math.Round(target.Y), 0.0f) * Matrix.CreateRotationZ(rotation) * - Matrix.CreateScale(new Vector3((float)zoom, (float)zoom, 1)) * + Matrix.CreateScale(new Vector3(zoom, zoom, 1)) * Matrix.CreateScale(scalingFactor) * Matrix.CreateTranslation(SceneManager.GraphicsDevice.Viewport.Width / 2, SceneManager.GraphicsDevice.Viewport.Height / 2, 0); @@ -152,12 +155,12 @@ namespace CLEngine.Core return result; } - /// - /// 计算给定游戏对象的摄像机的变换矩阵 - /// - /// 对象 - /// 变换矩阵 - public Matrix ObjectTransform(GameObject obj) + /// + /// 计算给定游戏对象的摄像机的变换矩阵 + /// + /// 对象 + /// 变换矩阵 + public Matrix ObjectTransform(GameObject obj) { return Matrix.CreateTranslation(-obj.Transform.Position.X, -obj.Transform.Position.Y, 0.0f) * diff --git a/Engine/CLEngine.Core/ColorExtensions.cs b/Engine/CLEngine.Core/ColorExtensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..129b79484b19dbf235934bf99be862e3d1b342c2 --- /dev/null +++ b/Engine/CLEngine.Core/ColorExtensions.cs @@ -0,0 +1,38 @@ +using Microsoft.Xna.Framework; + +namespace CLEngine.Core +{ + /// + /// Colore类扩展 + /// + public static class ColorExtensions + { + /// + /// 获得Color * alpha + /// + /// + /// + /// + public static Color WithAlpha(this Color color, float alpha) + => color * alpha; + + /// + /// 两种颜色之间的线性插值 + /// + /// + /// + /// + /// + public static Color Lerp(this Color value1, Color value2, float amount) + { + amount = MathHelper.Clamp(amount, 0, 1); + return new Color() + { + R = (byte)MathHelper.Lerp(value1.R, value2.R, amount), + G = (byte)MathHelper.Lerp(value1.G, value2.G, amount), + B = (byte)MathHelper.Lerp(value1.B, value2.B, amount), + A = (byte)MathHelper.Lerp(value1.A, value2.A, amount) + }; + } + } +} \ No newline at end of file diff --git a/Engine/CLEngine.Core/asset/FadeObject.cs b/Engine/CLEngine.Core/asset/FadeObject.cs new file mode 100644 index 0000000000000000000000000000000000000000..dba3b473bc7abf415a6c9f1b55eb3c36be4a439a --- /dev/null +++ b/Engine/CLEngine.Core/asset/FadeObject.cs @@ -0,0 +1,136 @@ +using System; +using Microsoft.Xna.Framework; + +namespace CLEngine.Core.asset +{ + /// + /// 随时间改变其不透明度的虚拟对象 + /// + public class FadeObject + { + private readonly TimeSpan _fadeDuration; + + private readonly Color _originalColor; + private TimeSpan _elapsed; + + /// + /// 淡出完成后触发 + /// + public event EventHandler FadeOutCompleted; + + /// + /// 淡入时完成触发 + /// + public event EventHandler FadeInCompleted; + + private FadeState _currentFadeState; + + /// + /// 对象的当前颜色,包括其不透明度 + /// + public Color OverlayColor { get; set; } + + /// + /// 如果正在进行淡入淡出则为真 + /// + public bool IsFading + => _currentFadeState != FadeState.Static; + + /// + /// 返回当前的不透明度值 + /// + public float CurrentAlpha { get; private set; } + + /// + /// 淡化对象构造函数 + /// + /// + /// + public FadeObject( + TimeSpan fadeDuration, + Color originalColor) + { + _fadeDuration = fadeDuration; + _originalColor = originalColor; + _currentFadeState = FadeState.Static; + CurrentAlpha = 1f; + } + + private enum FadeState + { + Static, + FadeIn, + FadeOut + } + + /// + /// 启动此对象的淡入淡出动画 + /// + public void FadeIn() + { + _currentFadeState = FadeState.FadeIn; + CurrentAlpha = 0f; + OverlayColor = _originalColor.WithAlpha(CurrentAlpha); + _elapsed = TimeSpan.Zero; + } + + /// + /// 启动此对象的淡出动画 + /// + public void FadeOut() + { + _currentFadeState = FadeState.FadeOut; + CurrentAlpha = 1f; + OverlayColor = _originalColor.WithAlpha(CurrentAlpha); + _elapsed = TimeSpan.Zero; + } + + /// + /// 管理fadein / fadeout逻辑 + /// + /// + public void Update(TimeSpan elapsed) + { + switch (_currentFadeState) + { + case FadeState.FadeIn: + + _elapsed += elapsed; + + if (_elapsed >= _fadeDuration) + { + _currentFadeState = FadeState.Static; + CurrentAlpha = 1f; + OverlayColor = _originalColor.WithAlpha(CurrentAlpha); + FadeInCompleted?.Invoke(this, EventArgs.Empty); + } + else + { + CurrentAlpha = (float)(_elapsed.TotalSeconds / _fadeDuration.TotalSeconds); + OverlayColor = _originalColor.WithAlpha(CurrentAlpha); + } + + break; + + case FadeState.FadeOut: + + _elapsed += elapsed; + + if (_elapsed >= _fadeDuration) + { + _currentFadeState = FadeState.Static; + CurrentAlpha = 0f; + OverlayColor = _originalColor.WithAlpha(CurrentAlpha); + FadeOutCompleted?.Invoke(this, EventArgs.Empty); + } + else + { + CurrentAlpha = 1f - ((float)(_elapsed.TotalSeconds / _fadeDuration.TotalSeconds)); + OverlayColor = _originalColor.WithAlpha(CurrentAlpha); + } + + break; + } + } + } +} \ No newline at end of file diff --git a/Engine/CLEngine.Core/asset/SplashScreenLoader.cs b/Engine/CLEngine.Core/asset/SplashScreenLoader.cs new file mode 100644 index 0000000000000000000000000000000000000000..355f917b791c417daf3f7f76525d7d8c85fb258b --- /dev/null +++ b/Engine/CLEngine.Core/asset/SplashScreenLoader.cs @@ -0,0 +1,116 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; + +namespace CLEngine.Core.asset +{ + /// + /// 用于游戏启动画面 + /// 当加载完资源后全部释放 + /// + [SuppressMessage("ReSharper", "UnusedVariable")] + public class SplashScreenLoader + { + private readonly Action _loadFunction; + private readonly ContentManager _contentManager; + private readonly string _splashScreenPath; + private Sprite _splashScreenSprite; + private FadeObject _splashScreenFadingObject; + + /// + /// 定义了启动屏幕在屏幕上的最短时间 + /// + public TimeSpan MininumSplashScreenDuration { get; set; } = TimeSpan.FromSeconds(2); + + /// + /// 资产加载完成后引发事件 + /// + public event EventHandler Completed; + + /// + /// 构造启动画面加载器 + /// + /// 加载资产的功能 + /// 内容管理器用于加载初始屏幕图像 + /// 等待资源加载时显示的图像的启动画面路径 + public SplashScreenLoader( + Action loadFunction, + ContentManager contentManager, + string splashScreenPath) + { + if (string.IsNullOrWhiteSpace(splashScreenPath)) + { + throw new ArgumentNullException(nameof(splashScreenPath)); + } + + _splashScreenPath = splashScreenPath; + + _loadFunction = loadFunction ?? throw new ArgumentNullException(nameof(loadFunction)); + _contentManager = contentManager ?? throw new ArgumentNullException(nameof(contentManager)); + } + + /// + /// Starts loading the assets + /// + public void Load() + { + var splashScreenTexture = _contentManager.Load(_splashScreenPath); + + _splashScreenSprite = new Sprite {Texture = splashScreenTexture, DisplayMode = Sprite.DisplayModes.Fill}; + _splashScreenFadingObject = new FadeObject(TimeSpan.FromSeconds(1), Color.White); + _splashScreenFadingObject.FadeIn(); + _splashScreenFadingObject.FadeOutCompleted += SplashScreen_FadeOutCompleted; + + // 在后台加载资源 + var assetsLoadingTask = LoadAndManageSplashScreen(); + } + + /// + /// 当SplashScreen淡出完成时,表示资产已加载 + /// + /// + /// + private void SplashScreen_FadeOutCompleted(object sender, EventArgs e) + => Completed?.Invoke(this, EventArgs.Empty); + + private async Task LoadAndManageSplashScreen() + { + var loadTimer = new Stopwatch(); + loadTimer.Start(); + _loadFunction(); + loadTimer.Stop(); + + var lastingSplashScreenDuration = MininumSplashScreenDuration - loadTimer.Elapsed; + if (lastingSplashScreenDuration > TimeSpan.Zero) + { + await Task.Delay(lastingSplashScreenDuration).ConfigureAwait(false); + } + + // 加载完成后,淡出SplashScreen图像 + _splashScreenFadingObject.FadeOut(); + } + + /// + /// 它管理SplashScreen淡入淡出逻辑 + /// + /// + public void Update(TimeSpan elapsed) + => _splashScreenFadingObject.Update(elapsed); + + /// + /// 它绘制了SplashScreen + /// + /// + public void Draw(SpriteBatch spriteBatch) + { + spriteBatch.Draw( + _splashScreenSprite.Texture, + Vector2.Zero, + _splashScreenFadingObject.OverlayColor); + } + } +} \ No newline at end of file diff --git a/Engine/CLEngine.Core/gameObjects/Sprite.cs b/Engine/CLEngine.Core/gameObjects/Sprite.cs index 1afa713d07bf237acd27cbf168e1b0bbab5370f7..742a01c17e91954b773a8ae855b3fd58edb9828f 100644 --- a/Engine/CLEngine.Core/gameObjects/Sprite.cs +++ b/Engine/CLEngine.Core/gameObjects/Sprite.cs @@ -1,10 +1,11 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; -using CLEngine.Core; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +// ReSharper disable once CheckNamespace namespace CLEngine.Core { /// @@ -17,10 +18,28 @@ namespace CLEngine.Core [KnownType(typeof(Sprite))] public class Sprite : GameObject { - /// - /// 显示模式 - /// - public enum DisplayModes { None, Tile, PositionTile, Fill } + /// + /// 显示模式 + /// + public enum DisplayModes + { + /// + /// + /// + None, + /// + /// + /// + Tile, + /// + /// + /// + PositionTile, + /// + /// + /// + Fill + } [DataMember] private string imageName; @@ -32,7 +51,7 @@ namespace CLEngine.Core [DataMember] private DisplayModes displayMode = DisplayModes.None; - [DataMember] private bool useCustomOrigin = false; + [DataMember] private bool useCustomOrigin; [DataMember] private Color color = Color.White; @@ -87,6 +106,14 @@ namespace CLEngine.Core [DataMember] private SpriteEffects spriteEffect = SpriteEffects.None; + /// + /// + /// + public Sprite() + { + useCustomOrigin = false; + } + /// /// 原点 /// @@ -174,7 +201,7 @@ namespace CLEngine.Core public BlendModes BlendMode { get { return blendMode; } - set { blendMode = value; this.LoadState(); } + set { blendMode = value; LoadState(); } } /// @@ -190,6 +217,9 @@ namespace CLEngine.Core set { sourceRectangle = value; } } + /// + /// + /// public override void Initialize() { if (texture == null && imageName != null && imageName.Trim() != "") @@ -204,20 +234,26 @@ namespace CLEngine.Core private void LoadState() { - switch (this.blendMode) + switch (blendMode) { case BlendModes.NonPremultiplied: - this.blendState = BlendState.NonPremultiplied; + blendState = BlendState.NonPremultiplied; break; case BlendModes.AlphaBlend: - this.blendState = BlendState.AlphaBlend; + blendState = BlendState.AlphaBlend; break; case BlendModes.Additive: - this.blendState = BlendState.Additive; + blendState = BlendState.Additive; break; } } + /// + /// + /// + /// + /// + [SuppressMessage("ReSharper", "PossibleLossOfFraction")] public override void Draw(GameTime gameTime, SpriteBatch spriteBatch) { base.Draw(gameTime, spriteBatch); @@ -253,7 +289,7 @@ namespace CLEngine.Core //int sizeWidth = (int)(endPos.X - initialPosition.X); //int sizeHeight = (int)(endPos.Y - initialPosition.Y); - spriteBatch.Begin(SpriteSortMode.Deferred, this.blendState, SamplerState.LinearWrap, null, null, null, SceneManager.ActiveCamera.TransformMatrix); + spriteBatch.Begin(SpriteSortMode.Deferred, blendState, SamplerState.LinearWrap, null, null, null, SceneManager.ActiveCamera.TransformMatrix); //spriteBatch.Draw(texture, new Vector2(initialPosition.X, initialPosition.Y), new Rectangle((int)SceneManager.ActiveCamera.Position.X, // (int)SceneManager.ActiveCamera.Position.Y, sizeWidth, sizeHeight), Color, 0, Vector2.Zero, 1.0f, @@ -284,7 +320,7 @@ namespace CLEngine.Core //int sizeWidth = (int)(endPos.X - initialPosition.X); //int sizeHeight = (int)(endPos.Y - initialPosition.Y); - spriteBatch.Begin(SpriteSortMode.Deferred, this.blendState, SamplerState.LinearWrap, null, null, null, SceneManager.ActiveCamera.TransformMatrix); + spriteBatch.Begin(SpriteSortMode.Deferred, blendState, SamplerState.LinearWrap, null, null, null, SceneManager.ActiveCamera.TransformMatrix); //spriteBatch.Draw(texture, new Vector2(initialPosition.X, initialPosition.Y), new Rectangle((int)Transform.Position.X, // (int)Transform.Position.Y, sizeWidth, sizeHeight), Color, 0, Vector2.Zero, 1.0f, @@ -299,10 +335,10 @@ namespace CLEngine.Core Rectangle fill = new Rectangle( (int)SceneManager.ActiveScene.Camera.Position.X, (int)SceneManager.ActiveScene.Camera.Position.Y - 1, - (int)SceneManager.GameProject.Settings.ScreenWidth, - (int)SceneManager.GameProject.Settings.ScreenHeight); + SceneManager.GameProject.Settings.ScreenWidth, + SceneManager.GameProject.Settings.ScreenHeight); - spriteBatch.Begin(SpriteSortMode.Deferred, this.blendState, SamplerState.LinearClamp, null, null, null, SceneManager.ActiveCamera.TransformMatrix); + spriteBatch.Begin(SpriteSortMode.Deferred, blendState, SamplerState.LinearClamp, null, null, null, SceneManager.ActiveCamera.TransformMatrix); if (sourceRectangle == Rectangle.Empty) spriteBatch.Draw(texture, fill, null, Color, 0, Origin, spriteEffect, 0); @@ -311,7 +347,7 @@ namespace CLEngine.Core } else { - spriteBatch.Begin(SpriteSortMode.Deferred, this.blendState, SamplerState.LinearClamp, null, RasterizerState.CullNone, null, SceneManager.ActiveCamera.TransformMatrix); + spriteBatch.Begin(SpriteSortMode.Deferred, blendState, SamplerState.LinearClamp, null, RasterizerState.CullNone, null, SceneManager.ActiveCamera.TransformMatrix); //Console.Info("rr: " + Transform.Rotation);