-
- using System;
- using Godot;
-
- /// <summary>
- /// 静态图像画布类, 用于处理游戏中大量静态物体的解决方案<br/>
- /// 将物体纹理绘直接绘制到画布上, 这样大大减少GPU开销, 从而提高帧率<br/>
- /// 图像旋转遵循完美像素
- /// </summary>
- public partial class ImageCanvas : Sprite2D, IDestroy
- {
- /// <summary>
- /// 画布大小
- /// </summary>
- public int Width { get; }
-
- /// <summary>
- /// 画布高度
- /// </summary>
- public int Height { get; }
-
- public bool IsDestroyed { get; private set; }
-
- private Image _canvas;
- private ImageTexture _texture;
-
- public ImageCanvas(int width, int height)
- {
- Width = width;
- Height = height;
-
- _canvas = Image.Create(width, height, false, Image.Format.Rgba8);
- _texture = ImageTexture.CreateFromImage(_canvas);
- }
-
- public override void _Ready()
- {
- Centered = false;
- Texture = _texture;
- }
-
- /// <summary>
- /// 将指定纹理添加到预渲染队列中, 完成后会调用 onDrawingComplete 回调函数
- /// </summary>
- /// <param name="texture">需要渲染的纹理</param>
- /// <param name="material">渲染材质, 不需要则传null</param>
- /// <param name="x">离画布左上角x坐标</param>
- /// <param name="y">离画布左上角y坐标</param>
- /// <param name="angle">旋转角度, 角度制</param>
- /// <param name="centerX">旋转中心点x</param>
- /// <param name="centerY">旋转中心点y</param>
- /// <param name="flipY">是否翻转y轴</param>
- /// <param name="onDrawingComplete">绘制完成的回调函数</param>
- public void DrawImageInCanvas(Texture2D texture, Material material, float x, float y, float angle, int centerX, int centerY, bool flipY, Action onDrawingComplete = null)
- {
- DrawImageInCanvas(texture, material, x, y, angle, centerX, centerY, flipY, true, onDrawingComplete);
- }
- private void DrawImageInCanvas(Texture2D texture, Material material, float x, float y, float angle, int centerX, int centerY, bool flipY, bool enableQueueCutting, Action onDrawingComplete)
- {
- var item = new ImageRenderData();
- item.OnDrawingComplete = onDrawingComplete;
- item.EnableQueueCutting = enableQueueCutting;
- item.ImageCanvas = this;
- item.SrcImage = texture.GetImage();
- item.Material = material;
- var width = item.SrcImage.GetWidth();
- var height = item.SrcImage.GetHeight();
- if (width > 128)
- {
- Debug.LogError("警告: 图像宽度大于 128, 旋转后像素点可能绘制到画布外导致像素丢失!");
- }
- if (height > 128)
- {
- Debug.LogError("警告: 图像高度大于 128, 旋转后像素点可能绘制到画布外导致像素丢失!");
- }
- item.X = Mathf.RoundToInt(x);
- item.Y = Mathf.RoundToInt(y);
- item.Rotation = Mathf.DegToRad(Mathf.RoundToInt(Utils.ConvertAngle(angle)));
- item.CenterX = centerX;
- item.CenterY = centerY;
- if (item.Rotation > Mathf.Pi)
- {
- item.CenterX = width - item.CenterX;
- item.CenterY = height - item.CenterY;
- item.Rotation -= Mathf.Pi;
- item.RotationGreaterThanPi = true;
- }
- item.FlipY = flipY;
-
- var cosAngle = Mathf.Cos(item.Rotation);
- var sinAngle = Mathf.Sin(item.Rotation);
- if (cosAngle == 0)
- {
- cosAngle = 1e-6f;
- }
-
- if (sinAngle == 0)
- {
- sinAngle = 1e-6f;
- }
-
- //旋转后的图片宽高
- item.RenderWidth = Mathf.CeilToInt(width * Mathf.Abs(cosAngle) + height * sinAngle) + 2;
- item.RenderHeight = Mathf.CeilToInt(width * sinAngle + height * Mathf.Abs(cosAngle)) + 2;
-
- if (item.RenderWidth >= RenderViewportSize.X)
- {
- Debug.LogError($"图像旋转后的宽度大于{RenderViewportSize.X}, 不支持绘制到 ImageCanvas 下!");
- item.SrcImage.Dispose();
- return;
- }
-
- //旋转后的图像中心点偏移
- 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;
-
- _queueItems.Add(item);
- }
-
- /// <summary>
- /// 将指定 ActivityObject 添加到预渲染队列中, 完成后会调用 onDrawingComplete 回调函数
- /// </summary>
- /// <param name="activityObject">物体实例</param>
- /// <param name="x">离画布左上角x坐标</param>
- /// <param name="y">离画布左上角y坐标</param>
- /// <param name="onDrawingComplete">绘制完成的回调</param>
- public void DrawActivityObjectInCanvas(ActivityObject activityObject, float x, float y, Action onDrawingComplete = null)
- {
- //是否翻转y轴
- var flipY = activityObject.Scale.Y < 0;
-
- var animatedSprite = activityObject.AnimatedSprite;
- var animatedSpritePosition = animatedSprite.Position;
- var ax = x + animatedSpritePosition.X;
- var ay = y + animatedSpritePosition.X;
-
- //先绘制阴影
- var shadowSprite = activityObject.ShadowSprite;
- var shadowSpriteTexture = activityObject.ShadowSprite.Texture;
- if (shadowSpriteTexture != null)
- {
- var spriteOffset = shadowSprite.Offset;
- var centerX = (int)-spriteOffset.X;
- var centerY = (int)-spriteOffset.Y;
- var angle = Utils.ConvertAngle(shadowSprite.GlobalRotationDegrees);
- if (shadowSprite.Centered)
- {
- centerX += shadowSpriteTexture.GetWidth() / 2;
- centerY += shadowSpriteTexture.GetHeight() / 2;
- }
-
- DrawImageInCanvas(
- shadowSprite.Texture, shadowSprite.Material,
- ax + activityObject.ShadowOffset.X, ay + activityObject.ShadowOffset.Y,
- angle,
- centerX, centerY, flipY,
- true, null
- );
- }
-
- //再绘制纹理
- var texture = activityObject.GetCurrentTexture();
- if (texture != null)
- {
- var spriteOffset = animatedSprite.Offset;
- var centerX = (int)-spriteOffset.X;
- var centerY = (int)-spriteOffset.Y;
- if (animatedSprite.Centered)
- {
- centerX += texture.GetWidth() / 2;
- centerY += texture.GetHeight() / 2;
- }
- //为了保证阴影在此之前渲染, 所以必须关闭插队渲染
- DrawImageInCanvas(
- texture, animatedSprite.Material,
- ax, ay,
- animatedSprite.GlobalRotationDegrees,
- centerX, centerY, flipY,
- false, onDrawingComplete
- );
- }
- }
-
- public void Destroy()
- {
- if (IsDestroyed)
- {
- return;
- }
-
- IsDestroyed = true;
- QueueFree();
- _canvas.Dispose();
- _texture.Dispose();
- }
-
- /// <summary>
- /// 使用透明色替换掉整个画布
- /// </summary>
- public void Clear()
- {
- Clear(new Color(0, 0, 0, 0));
- }
-
- /// <summary>
- /// 使用指定颜色替换掉整个画布
- /// </summary>
- public void Clear(Color color)
- {
- _canvas.Fill(color);
- Redraw();
- }
-
- private void Redraw()
- {
- _texture.Update(_canvas);
- }
- }