diff --git a/DungeonShooting_Godot/project.godot b/DungeonShooting_Godot/project.godot
index 601f8cb..fac0b6e 100644
--- a/DungeonShooting_Godot/project.godot
+++ b/DungeonShooting_Godot/project.godot
@@ -11,7 +11,7 @@
[application]
config/name="DungeonShooting"
-run/main_scene="res://scene/Main.tscn"
+run/main_scene="res://scene/test/TestOptimizeSprite.tscn"
config/features=PackedStringArray("4.1", "C#")
config/icon="res://icon.png"
diff --git a/DungeonShooting_Godot/scene/test/TestOptimizeSprite.tscn b/DungeonShooting_Godot/scene/test/TestOptimizeSprite.tscn
index 0ac97b4..2122239 100644
--- a/DungeonShooting_Godot/scene/test/TestOptimizeSprite.tscn
+++ b/DungeonShooting_Godot/scene/test/TestOptimizeSprite.tscn
@@ -11,6 +11,16 @@
[ext_resource type="Texture2D" uid="uid://yn8t7ovmt4gj" path="res://resource/sprite/weapon/gun1.png" id="9_mddgj"]
[ext_resource type="Texture2D" uid="uid://5geiuvv6hyov" path="res://resource/sprite/weapon/gun2.png" id="10_rxwsh"]
-[node name="TestOptimizeSprite" type="Node2D"]
+[node name="TestOptimizeSprite" type="Node2D" node_paths=PackedStringArray("SubViewport", "ViewCamera")]
script = ExtResource("1_rwuav")
ImageList = Array[Texture2D]([ExtResource("2_qvs0g"), ExtResource("3_jejmg"), ExtResource("4_twsss"), ExtResource("5_bdcjp"), ExtResource("6_sqcu7"), ExtResource("7_w4iwy"), ExtResource("8_csd6d"), ExtResource("9_mddgj"), ExtResource("10_rxwsh")])
+SubViewport = NodePath("SubViewport")
+ViewCamera = NodePath("SubViewport/Camera2D")
+
+[node name="SubViewport" type="SubViewport" parent="."]
+transparent_bg = true
+canvas_item_default_texture_filter = 0
+render_target_update_mode = 4
+
+[node name="Camera2D" type="Camera2D" parent="SubViewport"]
+anchor_mode = 0
diff --git a/DungeonShooting_Godot/src/framework/map/image/ImageCanvas.cs b/DungeonShooting_Godot/src/framework/map/image/ImageCanvas.cs
index 0e8f50d..80eeea2 100644
--- a/DungeonShooting_Godot/src/framework/map/image/ImageCanvas.cs
+++ b/DungeonShooting_Godot/src/framework/map/image/ImageCanvas.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
using Godot;
public partial class ImageCanvas : Sprite2D, IDestroy
@@ -28,7 +26,7 @@
_canvas = Image.Create(width, height, false, Image.Format.Rgba8);
_texture = ImageTexture.CreateFromImage(_canvas);
- _canvas.Fill(Colors.Gray);
+ //_canvas.Fill(Colors.Gray);
var w = _canvas.GetWidth();
var h = _canvas.GetHeight();
for (int i = 0; i < w; i++)
@@ -60,12 +58,12 @@
/// 是否翻转y轴
public void DrawImageInCanvas(Texture2D texture, int x, int y, float angle, int centerX, int centerY, bool flipY)
{
- var item = new EnqueueItem();
- item.Canvas = this;
- item.Image = texture.GetImage();
+ var item = new ImageRenderData();
+ item.ImageCanvas = this;
+ item.SrcImage = texture.GetImage();
item.X = x;
item.Y = y;
- item.Angle = angle;
+ item.Rotation = Mathf.DegToRad(Mathf.RoundToInt(Utils.ConvertAngle(angle)));
item.CenterX = centerX;
item.CenterY = centerY;
item.FlipY = flipY;
@@ -81,6 +79,9 @@
}
IsDestroyed = true;
+ QueueFree();
+ _canvas.Dispose();
+ _texture.Dispose();
}
private void Redraw()
@@ -88,204 +89,8 @@
_texture.Update(_canvas);
}
- private void HandlerDrawImageInCanvas(EnqueueItem item)
+ private void HandlerDrawImageInCanvas(ImageRenderData item)
{
- var image = item.Image;
- var newAngle = Mathf.RoundToInt(Utils.ConvertAngle(item.Angle));
-
- if (newAngle == 0) //原图, 直接画上去
- {
- if (item.FlipY)
- {
- image.FlipY();
- }
-
- if (image.GetFormat() != Image.Format.Rgba8)
- {
- image.Convert(Image.Format.Rgba8);
- }
- _canvas.BlendRect(image, new Rect2I(0, 0, image.GetWidth(), image.GetHeight()),
- new Vector2I(item.X - item.CenterX, item.Y - item.CenterY));
- }
- else //其他角度
- {
- if (newAngle <= 180)
- {
- if (item.FlipY)
- {
- image.FlipY();
- }
-
- DrawRotateImage(image, item.X, item.Y, newAngle, item.CenterX, item.CenterY);
- }
- else
- {
- image.FlipX();
- if (!item.FlipY)
- {
- image.FlipY();
- }
-
- DrawRotateImage(
- image, item.X, item.Y,
- newAngle - 180,
- image.GetWidth() - item.CenterX - 1, image.GetHeight() - item.CenterY - 1
- );
- }
- }
-
- image.Dispose();
- item.Image = null;
- }
-
- private void DrawRotateImage(Image origin, int x, int y, int angle, int centerX, int centerY)
- {
- var rotation = Mathf.DegToRad(angle);
- var width = origin.GetWidth();
- var height = origin.GetHeight();
-
- var cosAngle = Mathf.Cos(rotation);
- var sinAngle = Mathf.Sin(rotation);
- if (cosAngle == 0)
- {
- cosAngle = -1e-6f;
- }
-
- if (sinAngle == 0)
- {
- sinAngle = 1e-6f;
- }
-
- var newWidth = Mathf.RoundToInt(width * Mathf.Abs(cosAngle) + height * sinAngle);
- var newHeight = Mathf.RoundToInt(width * sinAngle + height * Mathf.Abs(cosAngle));
-
- var offsetX =
- Mathf.RoundToInt((centerX / sinAngle +
- (0.5f * newWidth * sinAngle - 0.5f * newHeight * cosAngle + 0.5f * height) /
- cosAngle -
- (-0.5f * newWidth * cosAngle - 0.5f * newHeight * sinAngle + 0.5f * width) /
- sinAngle - centerY / cosAngle) /
- (cosAngle / sinAngle + sinAngle / cosAngle));
-
-
- var offsetY =
- Mathf.RoundToInt((centerX / cosAngle -
- (-0.5f * newWidth * cosAngle - 0.5f * newHeight * sinAngle + 0.5f * width) /
- cosAngle -
- (0.5f * newWidth * sinAngle - 0.5f * newHeight * cosAngle + 0.5f * height) /
- sinAngle + centerY / sinAngle) /
- (sinAngle / cosAngle + cosAngle / sinAngle));
-
- var num1 = -0.5f * newWidth * cosAngle - 0.5f * newHeight * sinAngle + 0.5f * width;
- var num2 = 0.5f * newWidth * sinAngle - 0.5f * newHeight * cosAngle + 0.5f * height;
-
- var cw = _canvas.GetWidth();
- var ch = _canvas.GetHeight();
- var numX = Mathf.Min(num1, (newWidth - 1) * cosAngle + (newHeight - 1) * sinAngle + num1);
- var numY = Mathf.Min(num2, -(newWidth - 1) * sinAngle + (newHeight - 1) * cosAngle + num2);
-
- for (var x2 = 0; x2 < newWidth; x2++)
- {
- for (var y2 = 0; y2 < newHeight; y2++)
- {
- //映射到原图的位置
- // var x1 = x2 * cosAngle + y2 * sinAngle + num1;
- // var y1 = -x2 * sinAngle + y2 * cosAngle + num2;
-
-
- var x1 = Mathf.FloorToInt(x2 * cosAngle + y2 * sinAngle + num1 - numX);
- var y1 = Mathf.FloorToInt(-x2 * sinAngle + y2 * cosAngle + num2 - numY);
-
- if (x1 < 0 || x1 >= width || y1 < 0 || y1 >= height)
- {
- //在图片外
- continue;
- }
-
-
- var cx = x2 + x - offsetX;
- if (cx < 0 || cx >= cw)
- {
- continue;
- }
-
- var cy = y2 + y - offsetY;
- if (cy < 0 || cy >= ch)
- {
- continue;
- }
-
- //_canvas.SetPixel(cx, cy, origin.GetPixel(x1, y1));
- _canvas.SetPixel(cx, cy, _canvas.GetPixel(cx, cy).Blend(origin.GetPixel(
- //Mathf.RoundToInt(x1), Mathf.RoundToInt(y1)
- x1, y1
- )));
- }
- }
- }
-
- //--------------------------------------------------------------------------------------------------------------
-
- private class EnqueueItem
- {
- public ImageCanvas Canvas;
- public Image Image;
- public int X;
- public int Y;
- public float Angle;
- public int CenterX;
- public int CenterY;
- public bool FlipY;
- }
-
- ///
- /// 同一帧下将队列里的image绘制到指定画布下最大消耗时间, 如果绘制的时间超过了这个值, 则队列中后面的image将会放到下一帧绘制
- ///
- public static float MaxHandlerTime { get; set; } = 2f;
-
- private static readonly Queue _enqueueItems = new Queue();
- private static readonly HashSet _redrawCanvas = new HashSet();
-
- ///
- /// 根据重绘队列更新画布
- ///
- public static void UpdateImageCanvas(float delta)
- {
- if (_enqueueItems.Count > 0)
- {
- _redrawCanvas.Clear();
- var startTime = DateTime.Now;
-
- var c = _enqueueItems.Count;
- //执行绘制操作
- //绘制的总时间不能超过MaxHandlerTime, 如果超过了, 那就下一帧再画其它的吧
- do
- {
- var item = _enqueueItems.Dequeue();
- if (!item.Canvas.IsDestroyed)
- {
- item.Canvas.HandlerDrawImageInCanvas(item);
- _redrawCanvas.Add(item.Canvas);
- }
- } while (_enqueueItems.Count > 0 && (DateTime.Now - startTime).TotalMilliseconds < MaxHandlerTime);
-
- if (_enqueueItems.Count > 0)
- {
- GD.Print($"ImageCanvas: 还剩下{_enqueueItems.Count}个, 已经处理了{c - _enqueueItems.Count}个, 重绘画布{_redrawCanvas.Count}张");
- }
- else
- {
- GD.Print($"ImageCanvas: 全部画完, 已经处理了{c - _enqueueItems.Count}个, 重绘画布{_redrawCanvas.Count}张");
- }
-
- //重绘画布
- foreach (var drawCanvas in _redrawCanvas)
- {
- drawCanvas.Redraw();
- }
-
- _redrawCanvas.Clear();
- }
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/map/image/ImageCanvas_Static.cs b/DungeonShooting_Godot/src/framework/map/image/ImageCanvas_Static.cs
new file mode 100644
index 0000000..1e63e71
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/map/image/ImageCanvas_Static.cs
@@ -0,0 +1,173 @@
+
+
+using System;
+using System.Collections.Generic;
+using Godot;
+
+public partial class ImageCanvas
+{
+
+ ///
+ /// 同一帧下将队列里的image绘制到指定画布下最大消耗时间, 如果绘制的时间超过了这个值, 则队列中后面的image将会放到下一帧绘制
+ ///
+ public static float MaxHandlerTime { get; set; } = 4f;
+
+ ///
+ /// 渲染窗口
+ ///
+ public static SubViewport RenderViewport { get; private set; }
+
+ ///
+ /// 渲染偏移位置
+ ///
+ public static Vector2 RenderOffset { get; set; }
+
+ //预渲染队列
+ private static readonly Queue _enqueueItems = new Queue();
+ //渲染中的队列
+ private static readonly Queue _drawingEnqueueItems = new Queue();
+
+ private static readonly Stack _renderSpriteStack = new Stack();
+
+ public static void Init(SubViewport renderViewport, Vector2 renderOffset)
+ {
+ RenderViewport = renderViewport;
+ RenderOffset = renderOffset;
+ RenderingServer.FramePostDraw += OnFramePostDraw;
+ }
+
+ private static ImageRenderSprite GetRenderSprite(Vector2 position)
+ {
+ ImageRenderSprite renderSprite;
+ if (_renderSpriteStack.Count > 0)
+ {
+ renderSprite = _renderSpriteStack.Pop();
+ }
+ else
+ {
+ renderSprite = new ImageRenderSprite();
+ }
+
+ renderSprite.Sprite.Position = position;
+ RenderViewport.AddChild(renderSprite.Sprite);
+ return renderSprite;
+ }
+
+ private static void ReclaimRenderSprite(ImageRenderSprite renderSprite)
+ {
+ RenderViewport.RemoveChild(renderSprite.Sprite);
+ _renderSpriteStack.Push(renderSprite);
+ }
+
+ private static void OnFramePostDraw()
+ {
+ //上一帧绘制的image
+ if (_drawingEnqueueItems.Count > 0)
+ {
+ var redrawCanvas = new HashSet();
+ var viewportTexture = RenderViewport.GetTexture();
+ using (var image = viewportTexture.GetImage())
+ {
+ var num = 0;
+ do
+ {
+ var item = _drawingEnqueueItems.Dequeue();
+ if (!item.ImageCanvas.IsDestroyed)
+ {
+ redrawCanvas.Add(item.ImageCanvas);
+ //截取Viewport像素点
+ item.ImageCanvas._canvas.BlendRect(image,
+ new Rect2I(0, 0, item.RenderWidth, item.RenderHeight),
+ new Vector2I(item.X - item.RenderOffsetX, item.Y - item.RenderOffsetY)
+ );
+
+ item.SrcImage.Dispose();
+ item.SrcImage = null;
+ num++;
+ }
+
+ //回收 RenderSprite
+ if (item.RenderSprite != null)
+ {
+ ReclaimRenderSprite(item.RenderSprite);
+ }
+ } while (_drawingEnqueueItems.Count > 0);
+
+ GD.Print($"当前帧绘制完成数量: {num}, 绘制队列数量: {_drawingEnqueueItems.Count}");
+ }
+
+ //重绘画布
+ foreach (var drawCanvas in redrawCanvas)
+ {
+ drawCanvas.Redraw();
+ }
+ }
+
+ //处理下一批image
+ if (_enqueueItems.Count > 0)
+ {
+ var startTime = DateTime.Now;
+
+ var num = 0;
+ //执行绘制操作
+ //绘制的总时间不能超过MaxHandlerTime, 如果超过了, 那就下一帧再画其它的吧
+ do
+ {
+ var item = _enqueueItems.Dequeue();
+ if (!item.ImageCanvas.IsDestroyed)
+ {
+
+ var cosAngle = Mathf.Cos(item.Rotation);
+ var sinAngle = Mathf.Sin(item.Rotation);
+ if (cosAngle == 0)
+ {
+ cosAngle = 1e-6f;
+ }
+
+ if (sinAngle == 0)
+ {
+ sinAngle = 1e-6f;
+ }
+
+ var width = item.SrcImage.GetWidth();
+ var height = item.SrcImage.GetHeight();
+ //旋转后的图片宽高
+ item.RenderWidth = Mathf.CeilToInt(width * Mathf.Abs(cosAngle) + height * sinAngle) + 2;
+ item.RenderHeight = Mathf.CeilToInt(width * sinAngle + height * Mathf.Abs(cosAngle)) + 2;
+
+ item.RenderOffsetX =
+ (int)((item.CenterX / sinAngle +
+ (0.5f * item.RenderWidth * sinAngle - 0.5f * item.RenderHeight * cosAngle +
+ 0.5f * height) /
+ cosAngle -
+ (-0.5f * item.RenderWidth * cosAngle - 0.5f * item.RenderHeight * sinAngle +
+ 0.5f * width) /
+ sinAngle - item.CenterY / cosAngle) /
+ (cosAngle / sinAngle + sinAngle / cosAngle)) + 1;
+
+
+ item.RenderOffsetY =
+ (int)((item.CenterX / cosAngle -
+ (-0.5f * item.RenderWidth * cosAngle - 0.5f * item.RenderHeight * sinAngle + 0.5f * width) /
+ cosAngle -
+ (0.5f * item.RenderWidth * sinAngle - 0.5f * item.RenderHeight * cosAngle + 0.5f * height) /
+ sinAngle + item.CenterY / sinAngle) /
+ (sinAngle / cosAngle + cosAngle / sinAngle)) + 1;
+
+ var renderSprite = GetRenderSprite(new Vector2(0 + item.RenderOffsetX, 0 + item.RenderOffsetY));
+ item.RenderSprite = renderSprite;
+ //设置绘制信息
+ renderSprite.Sprite.Offset = new Vector2(item.CenterX, item.CenterY);
+ renderSprite.Sprite.Rotation = item.Rotation;
+
+ renderSprite.SetImage(item.SrcImage);
+ _drawingEnqueueItems.Enqueue(item);
+ num++;
+ }
+
+ } while (_enqueueItems.Count > 0 && (DateTime.Now - startTime).TotalMilliseconds < MaxHandlerTime);
+
+ GD.Print($"当前帧进入绘制绘队列数量: {num}, 待绘制队列数量: {_enqueueItems.Count}, 绘制队列数量: {_drawingEnqueueItems.Count}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/map/image/ImageRenderData.cs b/DungeonShooting_Godot/src/framework/map/image/ImageRenderData.cs
new file mode 100644
index 0000000..da66b00
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/map/image/ImageRenderData.cs
@@ -0,0 +1,29 @@
+
+
+using Godot;
+
+public class ImageRenderData
+{
+ ///
+ /// 指定的画布
+ ///
+ public ImageCanvas ImageCanvas;
+ ///
+ /// 需要绘制的原图
+ ///
+ public Image SrcImage;
+ public int X;
+ public int Y;
+ public float Rotation;
+ public int CenterX;
+ public int CenterY;
+ public bool FlipY;
+
+ //----------------------------------------
+ public ImageRenderSprite RenderSprite;
+ public int RenderWidth;
+ public int RenderHeight;
+
+ public int RenderOffsetX;
+ public int RenderOffsetY;
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/map/image/ImageRenderSprite.cs b/DungeonShooting_Godot/src/framework/map/image/ImageRenderSprite.cs
new file mode 100644
index 0000000..a5eb803
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/map/image/ImageRenderSprite.cs
@@ -0,0 +1,38 @@
+
+using Godot;
+
+public class ImageRenderSprite
+{
+ public Sprite2D Sprite { get; }
+
+ public ImageTexture Texture { get; }
+
+ private static Image _emptyImage;
+ private static Image EmptyImage
+ {
+ get
+ {
+ if (_emptyImage == null)
+ {
+ _emptyImage = Image.Create(1, 1, false, Image.Format.Rgba8);
+ }
+
+ return _emptyImage;
+ }
+ }
+
+ public ImageRenderSprite()
+ {
+ var sprite = new Sprite2D();
+ Sprite = sprite;
+ Texture = ImageTexture.CreateFromImage(EmptyImage);
+ sprite.Name = "RenderSprite";
+ sprite.Texture = Texture;
+ sprite.Centered = false;
+ }
+
+ public void SetImage(Image image)
+ {
+ Texture.SetImage(image);
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/test/TestOptimizeSprite.cs b/DungeonShooting_Godot/src/test/TestOptimizeSprite.cs
index 664adf1..f9dda88 100644
--- a/DungeonShooting_Godot/src/test/TestOptimizeSprite.cs
+++ b/DungeonShooting_Godot/src/test/TestOptimizeSprite.cs
@@ -4,45 +4,48 @@
{
[Export()] public Texture2D[] ImageList;
+ [Export()] public SubViewport SubViewport;
+
+ [Export()] public Camera2D ViewCamera;
+
public override void _Ready()
{
- ImageCanvas.MaxHandlerTime = 16;
+ ImageCanvas.Init(SubViewport, ViewCamera.Position);
+ ImageCanvas.MaxHandlerTime = 4;
+
var scale = 10;
var imageCanvas = new ImageCanvas(1920 / scale, 1080 / scale);
imageCanvas.Scale = new Vector2(scale, scale);
var delta = 360f / (15 * 8);
var angle = 0f;
- for (int i = 0; i < 15; i++)
- {
- for (int j = 0; j < 8; j++)
- {
- //var texture = Utils.RandomChoose(ImageList);
- var texture = ImageList[6];
- var centerX = 0;
- var centerY = 0;
- //var angle = Utils.RandomRangeInt(0, 360);
- GD.Print($"x: {i}, y: {j}, angle: " + angle);
- imageCanvas.DrawImageInCanvas(texture,
- //Utils.RandomRangeInt(0, imageCanvas.Width), Utils.RandomRangeInt(0, imageCanvas.Height),
- 10 + i * 10, 10 + j * 10,
- angle, centerX, centerY, false
- );
- angle += delta;
- }
- }
+
+
+
+ // for (int i = 0; i < 15; i++)
+ // {
+ // for (int j = 0; j < 8; j++)
+ // {
+ // //var texture = Utils.RandomChoose(ImageList);
+ // var texture = ImageList[6];
+ // var centerX = 0;
+ // var centerY = 0;
+ // //var angle = Utils.RandomRangeInt(0, 360);
+ // GD.Print($"x: {i}, y: {j}, angle: " + angle);
+ // imageCanvas.DrawImageInCanvas(texture,
+ // //Utils.RandomRangeInt(0, imageCanvas.Width), Utils.RandomRangeInt(0, imageCanvas.Height),
+ // 10 + i * 10, 10 + j * 10,
+ // angle, centerX, centerY, false
+ // );
+ // angle += delta;
+ // }
+ // }
- //var texture = ImageList[6];
- //imageCanvas.DrawImageInCanvas(texture, imageCanvas.Width / 2, imageCanvas.Height / 2, 0, 0, 0, false);
+ var texture = ImageList[0];
+ imageCanvas.DrawImageInCanvas(texture, imageCanvas.Width / 2, imageCanvas.Height / 2, 45, 0, 0, false);
//imageCanvas.DrawImageInCanvas(texture, imageCanvas.Width / 2, imageCanvas.Height / 2, 45, texture.GetWidth() - 1, texture.GetHeight() - 1, false);
AddChild(imageCanvas);
}
-
-
- public override void _Process(double delta)
- {
- ImageCanvas.UpdateImageCanvas((float)delta);
- }
}