mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2026-01-06 07:50:22 +01:00
2.5D Editor Viewport for Mono C#
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -53,6 +53,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="addons\node25d-cs\Basis25D.cs" />
|
||||
<Compile Include="addons\node25d-cs\main_screen\Gizmo25D.cs" />
|
||||
<Compile Include="addons\node25d-cs\Node25D.cs" />
|
||||
<Compile Include="addons\node25d-cs\ShadowMath25D.cs" />
|
||||
<Compile Include="addons\node25d-cs\Transform25D.cs" />
|
||||
@@ -63,4 +64,4 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -83,6 +83,10 @@ public class Node25D : Node2D, IComparable<Node25D>
|
||||
/// </summary>
|
||||
protected void Node25DProcess()
|
||||
{
|
||||
if (transform25D.basis == new Basis25D())
|
||||
{
|
||||
SetViewMode(0);
|
||||
}
|
||||
CheckViewMode();
|
||||
if (spatialNode != null)
|
||||
{
|
||||
@@ -92,34 +96,60 @@ public class Node25D : Node2D, IComparable<Node25D>
|
||||
{
|
||||
spatialNode = GetChild<Spatial>(0);
|
||||
}
|
||||
|
||||
GlobalPosition = transform25D.FlatPosition;
|
||||
}
|
||||
|
||||
public void SetViewMode(int viewModeIndex)
|
||||
{
|
||||
switch (viewModeIndex)
|
||||
{
|
||||
case 0:
|
||||
transform25D.basis = Basis25D.FortyFive * SCALE;
|
||||
break;
|
||||
case 1:
|
||||
transform25D.basis = Basis25D.Isometric * SCALE;
|
||||
break;
|
||||
case 2:
|
||||
transform25D.basis = Basis25D.TopDown * SCALE;
|
||||
break;
|
||||
case 3:
|
||||
transform25D.basis = Basis25D.FrontSide * SCALE;
|
||||
break;
|
||||
case 4:
|
||||
transform25D.basis = Basis25D.ObliqueY * SCALE;
|
||||
break;
|
||||
case 5:
|
||||
transform25D.basis = Basis25D.ObliqueZ * SCALE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckViewMode()
|
||||
{
|
||||
if (Input.IsActionJustPressed("top_down_mode"))
|
||||
if (Input.IsActionJustPressed("forty_five_mode"))
|
||||
{
|
||||
transform25D.basis = Basis25D.TopDown * SCALE;
|
||||
}
|
||||
else if (Input.IsActionJustPressed("front_side_mode"))
|
||||
{
|
||||
transform25D.basis = Basis25D.FrontSide * SCALE;
|
||||
}
|
||||
else if (Input.IsActionJustPressed("forty_five_mode"))
|
||||
{
|
||||
transform25D.basis = Basis25D.FortyFive * SCALE;
|
||||
SetViewMode(0);
|
||||
}
|
||||
else if (Input.IsActionJustPressed("isometric_mode"))
|
||||
{
|
||||
transform25D.basis = Basis25D.Isometric * SCALE;
|
||||
SetViewMode(1);
|
||||
}
|
||||
else if (Input.IsActionJustPressed("top_down_mode"))
|
||||
{
|
||||
SetViewMode(2);
|
||||
}
|
||||
else if (Input.IsActionJustPressed("front_side_mode"))
|
||||
{
|
||||
SetViewMode(3);
|
||||
}
|
||||
else if (Input.IsActionJustPressed("oblique_y_mode"))
|
||||
{
|
||||
transform25D.basis = Basis25D.ObliqueY * SCALE;
|
||||
SetViewMode(4);
|
||||
}
|
||||
else if (Input.IsActionJustPressed("oblique_z_mode"))
|
||||
{
|
||||
transform25D.basis = Basis25D.ObliqueZ * SCALE;
|
||||
SetViewMode(5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
using Godot;
|
||||
|
||||
// This is identical to the GDScript version, yet it doesn't work.
|
||||
[Tool]
|
||||
public class Viewport25D : Control
|
||||
{
|
||||
private int zoomLevel = 0;
|
||||
private bool isPanning = false;
|
||||
private Vector2 panCenter;
|
||||
private Vector2 viewportCenter;
|
||||
private int viewModeIndex = 0;
|
||||
|
||||
// The type or namespace name 'EditorInterface' could not be found (are you missing a using directive or an assembly reference?)
|
||||
// No idea why this error shows up in VS Code. It builds fine...
|
||||
public EditorInterface editorInterface; // Set in node25d_plugin.gd
|
||||
private bool moving = false;
|
||||
|
||||
private Viewport viewport2d;
|
||||
private Viewport viewportOverlay;
|
||||
private ButtonGroup viewModeButtonGroup;
|
||||
private Label zoomLabel;
|
||||
private PackedScene gizmo25dScene;
|
||||
|
||||
public async override void _Ready()
|
||||
{
|
||||
// Give Godot a chance to fully load the scene. Should take two frames.
|
||||
//yield(get_tree(), "idle_frame");
|
||||
//yield(get_tree(), "idle_frame");
|
||||
await ToSignal(GetTree(), "idle_frame");
|
||||
await ToSignal(GetTree(), "idle_frame");
|
||||
var editedSceneRoot = GetTree().EditedSceneRoot;
|
||||
if (editedSceneRoot == null)
|
||||
{
|
||||
// Godot hasn't finished loading yet, so try loading the plugin again.
|
||||
//editorInterface.SetPluginEnabled("node25d", false);
|
||||
//editorInterface.SetPluginEnabled("node25d", true);
|
||||
return;
|
||||
}
|
||||
// Alright, we're loaded up. Now check if we have a valid world and assign it.
|
||||
var world2d = editedSceneRoot.GetViewport().World2d;
|
||||
if (world2d == GetViewport().World2d)
|
||||
{
|
||||
return; // This is the MainScreen25D scene opened in the editor!
|
||||
}
|
||||
viewport2d.World2d = world2d;
|
||||
|
||||
// Onready vars.
|
||||
viewport2d = GetNode<Viewport>("Viewport2D");
|
||||
viewportOverlay = GetNode<Viewport>("ViewportOverlay");
|
||||
viewModeButtonGroup = GetParent().GetNode("TopBar").GetNode("ViewModeButtons").GetNode<Button>("45Degree").Group;
|
||||
zoomLabel = GetParent().GetNode("TopBar").GetNode("Zoom").GetNode<Label>("ZoomPercent");
|
||||
gizmo25dScene = ResourceLoader.Load<PackedScene>("res://addons/node25d/main_screen/gizmo_25d.tscn");
|
||||
}
|
||||
|
||||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
if (editorInterface == null) // Something's not right... bail!
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// View mode polling.
|
||||
var viewModeChangedThisFrame = false;
|
||||
var newViewMode = viewModeButtonGroup.GetPressedButton().GetIndex();
|
||||
if (viewModeIndex != newViewMode)
|
||||
{
|
||||
viewModeIndex = newViewMode;
|
||||
viewModeChangedThisFrame = true;
|
||||
RecursiveChangeViewMode(GetTree().EditedSceneRoot);
|
||||
}
|
||||
|
||||
// Zooming.
|
||||
if (Input.IsMouseButtonPressed((int)ButtonList.WheelUp))
|
||||
{
|
||||
zoomLevel += 1;
|
||||
}
|
||||
else if (Input.IsMouseButtonPressed((int)ButtonList.WheelDown))
|
||||
{
|
||||
zoomLevel -= 1;
|
||||
}
|
||||
float zoom = GetZoomAmount();
|
||||
|
||||
// Viewport size.
|
||||
Vector2 size = GetGlobalRect().Size;
|
||||
viewport2d.Size = size;
|
||||
|
||||
// Viewport transform.
|
||||
Transform2D viewportTrans = Transform2D.Identity;
|
||||
viewportTrans.x *= zoom;
|
||||
viewportTrans.y *= zoom;
|
||||
viewportTrans.origin = viewportTrans.BasisXform(viewportCenter) + size / 2;
|
||||
viewport2d.CanvasTransform = viewportTrans;
|
||||
viewportOverlay.CanvasTransform = viewportTrans;
|
||||
|
||||
// Delete unused gizmos.
|
||||
var selection = editorInterface.GetSelection().GetSelectedNodes();
|
||||
var overlayChildren = viewportOverlay.GetChildren();
|
||||
foreach (Gizmo25D overlayChild in overlayChildren)
|
||||
{
|
||||
bool contains = false;
|
||||
foreach (Node selected in selection)
|
||||
{
|
||||
if (selected == overlayChild.node25d && !viewModeChangedThisFrame)
|
||||
{
|
||||
contains = true;
|
||||
}
|
||||
}
|
||||
if (!contains)
|
||||
{
|
||||
overlayChild.QueueFree();
|
||||
}
|
||||
}
|
||||
// Add new gizmos.
|
||||
foreach (Node sel in selection)
|
||||
{
|
||||
if (sel is Node25D selected)
|
||||
{
|
||||
var newNode = true;
|
||||
foreach (Gizmo25D overlayChild2 in overlayChildren)
|
||||
{
|
||||
if (selected == overlayChild2.node25d)
|
||||
{
|
||||
newNode = false;
|
||||
}
|
||||
}
|
||||
if (newNode)
|
||||
{
|
||||
Gizmo25D gizmo = (Gizmo25D)gizmo25dScene.Instance();
|
||||
viewportOverlay.AddChild(gizmo);
|
||||
gizmo.node25d = selected;
|
||||
gizmo.Initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This only accepts input when the mouse is inside of the 2.5D viewport.
|
||||
public override void _GuiInput(InputEvent inputEvent)
|
||||
{
|
||||
if (inputEvent is InputEventMouseButton mouseButtonEvent)
|
||||
{
|
||||
if (mouseButtonEvent.IsPressed())
|
||||
{
|
||||
if ((ButtonList)mouseButtonEvent.ButtonIndex == ButtonList.WheelUp)
|
||||
{
|
||||
zoomLevel += 1;
|
||||
AcceptEvent();
|
||||
}
|
||||
else if ((ButtonList)mouseButtonEvent.ButtonIndex == ButtonList.WheelDown)
|
||||
{
|
||||
zoomLevel -= 1;
|
||||
AcceptEvent();
|
||||
}
|
||||
else if ((ButtonList)mouseButtonEvent.ButtonIndex == ButtonList.Middle)
|
||||
{
|
||||
isPanning = true;
|
||||
panCenter = viewportCenter - mouseButtonEvent.Position;
|
||||
AcceptEvent();
|
||||
}
|
||||
else if ((ButtonList)mouseButtonEvent.ButtonIndex == ButtonList.Left)
|
||||
{
|
||||
var overlayChildren2 = viewportOverlay.GetChildren();
|
||||
foreach (Gizmo25D overlayChild in overlayChildren2)
|
||||
{
|
||||
overlayChild.wantsToMove = true;
|
||||
}
|
||||
AcceptEvent();
|
||||
}
|
||||
}
|
||||
else if ((ButtonList)mouseButtonEvent.ButtonIndex == ButtonList.Middle)
|
||||
{
|
||||
isPanning = false;
|
||||
AcceptEvent();
|
||||
}
|
||||
else if ((ButtonList)mouseButtonEvent.ButtonIndex == ButtonList.Left)
|
||||
{
|
||||
var overlayChildren3 = viewportOverlay.GetChildren();
|
||||
foreach (Gizmo25D overlayChild in overlayChildren3)
|
||||
{
|
||||
overlayChild.wantsToMove = false;
|
||||
}
|
||||
AcceptEvent();
|
||||
}
|
||||
}
|
||||
else if (inputEvent is InputEventMouseMotion mouseEvent)
|
||||
{
|
||||
if (isPanning)
|
||||
{
|
||||
viewportCenter = panCenter + mouseEvent.Position;
|
||||
AcceptEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RecursiveChangeViewMode(Node currentNode)
|
||||
{
|
||||
// TODO
|
||||
if (currentNode.HasMethod("SetViewMode"))
|
||||
{
|
||||
//currentNode.SetViewMode(viewModeIndex);
|
||||
}
|
||||
foreach (Node child in currentNode.GetChildren())
|
||||
{
|
||||
RecursiveChangeViewMode(child);
|
||||
}
|
||||
}
|
||||
|
||||
private float GetZoomAmount()
|
||||
{
|
||||
float zoomAmount = Mathf.Pow(1.05476607648f, zoomLevel); // 13th root of 2
|
||||
zoomLabel.Text = Mathf.Round(zoomAmount * 1000) / 10 + "%";
|
||||
return zoomAmount;
|
||||
}
|
||||
|
||||
public void OnZoomOutPressed()
|
||||
{
|
||||
zoomLevel -= 1;
|
||||
}
|
||||
|
||||
public void OnZoomInPressed()
|
||||
{
|
||||
zoomLevel += 1;
|
||||
}
|
||||
|
||||
public void OnZoomResetPressed()
|
||||
{
|
||||
zoomLevel = 0;
|
||||
}
|
||||
}
|
||||
168
mono/2.5d/addons/node25d-cs/main_screen/Gizmo25D.cs
Normal file
168
mono/2.5d/addons/node25d-cs/main_screen/Gizmo25D.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using Godot;
|
||||
|
||||
// This is identical to the GDScript version, yet it doesn't work.
|
||||
[Tool]
|
||||
public class Gizmo25D : Node2D
|
||||
{
|
||||
// Not pixel perfect for all axes in all modes, but works well enough.
|
||||
// Rounding is not done until after the movement is finished.
|
||||
private const bool RoughlyRoundToPixels = true;
|
||||
|
||||
// Set when the node is created.
|
||||
public Node25D node25d;
|
||||
public Spatial spatialNode;
|
||||
|
||||
// Input from Viewport25D, represents if the mouse is clicked.
|
||||
public bool wantsToMove = false;
|
||||
|
||||
// Used to control the state of movement.
|
||||
private bool _moving = false;
|
||||
private Vector2 _startPosition = Vector2.Zero;
|
||||
|
||||
// Stores state of closest or currently used axis.
|
||||
private int dominantAxis;
|
||||
|
||||
private Node2D linesRoot;
|
||||
private Line2D[] lines = new Line2D[3];
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
linesRoot = GetChild<Node2D>(0);
|
||||
lines[0] = linesRoot.GetChild<Line2D>(0);
|
||||
lines[1] = linesRoot.GetChild<Line2D>(1);
|
||||
lines[2] = linesRoot.GetChild<Line2D>(2);
|
||||
}
|
||||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
if (lines == null)
|
||||
{
|
||||
return; // Somehow this node hasn't been set up yet.
|
||||
}
|
||||
if (node25d == null)
|
||||
{
|
||||
return; // We're most likely viewing the Gizmo25D scene.
|
||||
}
|
||||
// While getting the mouse position works in any viewport, it doesn't do
|
||||
// anything significant unless the mouse is in the 2.5D viewport.
|
||||
Vector2 mousePosition = GetLocalMousePosition();
|
||||
if (!_moving)
|
||||
{
|
||||
// If the mouse is farther than this many pixels, it won't grab anything.
|
||||
float closestDistance = 20.0f;
|
||||
dominantAxis = -1;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// Unrelated, but needs a loop too.
|
||||
Color modulateLine = lines[i].Modulate;
|
||||
modulateLine.a = 0.8f;
|
||||
lines[i].Modulate = modulateLine;
|
||||
|
||||
var distance = DistanceToSegmentAtIndex(i, mousePosition);
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
closestDistance = distance;
|
||||
dominantAxis = i;
|
||||
}
|
||||
}
|
||||
if (dominantAxis == -1)
|
||||
{
|
||||
// If we're not hovering over a line, ensure they are placed correctly.
|
||||
linesRoot.GlobalPosition = node25d.GlobalPosition;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Color modulate = lines[dominantAxis].Modulate;
|
||||
modulate.a = 1;
|
||||
lines[dominantAxis].Modulate = modulate;
|
||||
|
||||
if (!wantsToMove)
|
||||
{
|
||||
_moving = false;
|
||||
}
|
||||
else if (wantsToMove && !_moving)
|
||||
{
|
||||
_moving = true;
|
||||
_startPosition = mousePosition;
|
||||
}
|
||||
|
||||
if (_moving)
|
||||
{
|
||||
// Change modulate of unselected axes.
|
||||
modulate = lines[(dominantAxis + 1) % 3].Modulate;
|
||||
modulate.a = 0.5f;
|
||||
lines[(dominantAxis + 1) % 3].Modulate = modulate;
|
||||
lines[(dominantAxis + 2) % 3].Modulate = modulate;
|
||||
|
||||
// Calculate mouse movement and reset for next frame.
|
||||
var mouseDiff = mousePosition - _startPosition;
|
||||
_startPosition = mousePosition;
|
||||
// Calculate movement.
|
||||
var projectedDiff = mouseDiff.Project(lines[dominantAxis].Points[1]);
|
||||
var movement = projectedDiff.Length() / Node25D.SCALE;
|
||||
if (Mathf.IsEqualApprox(Mathf.Pi, projectedDiff.AngleTo(lines[dominantAxis].Points[1])))
|
||||
{
|
||||
movement *= -1;
|
||||
}
|
||||
// Apply movement.
|
||||
Transform t = spatialNode.Transform;
|
||||
t.origin += t.basis[dominantAxis] * movement;
|
||||
spatialNode.Transform = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure the gizmo is located at the object.
|
||||
GlobalPosition = node25d.GlobalPosition;
|
||||
if (RoughlyRoundToPixels)
|
||||
{
|
||||
Transform t = spatialNode.Transform;
|
||||
t.origin = (t.origin * Node25D.SCALE).Round() / Node25D.SCALE;
|
||||
spatialNode.Transform = t;
|
||||
}
|
||||
}
|
||||
// Move the gizmo lines appropriately.
|
||||
linesRoot.GlobalPosition = node25d.GlobalPosition;
|
||||
node25d.PropertyListChangedNotify();
|
||||
}
|
||||
|
||||
// Initializes after _ready due to the onready vars, called manually in Viewport25D.gd.
|
||||
// Sets up the points based on the basis values of the Node25D.
|
||||
public void Initialize()
|
||||
{
|
||||
var basis = node25d.Basis25D;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
lines[i].Points[1] = basis[i] * 3;
|
||||
}
|
||||
GlobalPosition = node25d.GlobalPosition;
|
||||
spatialNode = node25d.GetChild<Spatial>(0);
|
||||
}
|
||||
|
||||
|
||||
// Figures out if the mouse is very close to a segment. This method is
|
||||
// specialized for this script, it assumes that each segment starts at
|
||||
// (0, 0) and it provides a deadzone around the origin.
|
||||
private float DistanceToSegmentAtIndex(int index, Vector2 point)
|
||||
{
|
||||
if (lines == null)
|
||||
{
|
||||
return Mathf.Inf;
|
||||
}
|
||||
if (point.LengthSquared() < 400)
|
||||
{
|
||||
return Mathf.Inf;
|
||||
}
|
||||
|
||||
Vector2 segmentEnd = lines[index].Points[1];
|
||||
float lengthSquared = segmentEnd.LengthSquared();
|
||||
if (lengthSquared < 400)
|
||||
{
|
||||
return Mathf.Inf;
|
||||
}
|
||||
|
||||
var t = Mathf.Clamp(point.Dot(segmentEnd) / lengthSquared, 0, 1);
|
||||
var projection = t * segmentEnd;
|
||||
return point.DistanceTo(projection);
|
||||
}
|
||||
}
|
||||
23
mono/2.5d/addons/node25d-cs/main_screen/gizmo_25d.tscn
Normal file
23
mono/2.5d/addons/node25d-cs/main_screen/gizmo_25d.tscn
Normal file
@@ -0,0 +1,23 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/node25d-cs/main_screen/Gizmo25D.cs" type="Script" id=1]
|
||||
|
||||
[node name="Gizmo25D" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Lines" type="Node2D" parent="."]
|
||||
|
||||
[node name="X" type="Line2D" parent="Lines"]
|
||||
modulate = Color( 1, 1, 1, 0.8 )
|
||||
points = PoolVector2Array( 0, 0, 100, 0 )
|
||||
default_color = Color( 0.91, 0.273, 0, 1 )
|
||||
|
||||
[node name="Y" type="Line2D" parent="Lines"]
|
||||
modulate = Color( 1, 1, 1, 0.8 )
|
||||
points = PoolVector2Array( 0, 0, 0, -100 )
|
||||
default_color = Color( 0, 0.91, 0.273, 1 )
|
||||
|
||||
[node name="Z" type="Line2D" parent="Lines"]
|
||||
modulate = Color( 1, 1, 1, 0.8 )
|
||||
points = PoolVector2Array( 0, 0, 0, 100 )
|
||||
default_color = Color( 0.3, 0, 1, 1 )
|
||||
173
mono/2.5d/addons/node25d-cs/main_screen/main_screen_25d.tscn
Normal file
173
mono/2.5d/addons/node25d-cs/main_screen/main_screen_25d.tscn
Normal file
@@ -0,0 +1,173 @@
|
||||
[gd_scene load_steps=5 format=2]
|
||||
|
||||
[ext_resource path="res://addons/node25d-cs/main_screen/viewport_25d.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/node25d-cs/main_screen/view_mode_button_group.tres" type="ButtonGroup" id=2]
|
||||
|
||||
[sub_resource type="ViewportTexture" id=1]
|
||||
viewport_path = NodePath("Viewport25D/Viewport2D")
|
||||
|
||||
[sub_resource type="ViewportTexture" id=2]
|
||||
viewport_path = NodePath("Viewport25D/ViewportOverlay")
|
||||
|
||||
[node name="MainScreen25D" type="VBoxContainer"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="TopBar" type="HBoxContainer" parent="."]
|
||||
margin_right = 1600.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 0, 32 )
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ViewModeButtons" type="HBoxContainer" parent="TopBar"]
|
||||
margin_right = 798.0
|
||||
margin_bottom = 32.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="45Degree" type="CheckBox" parent="TopBar/ViewModeButtons"]
|
||||
margin_right = 94.0
|
||||
margin_bottom = 32.0
|
||||
pressed = true
|
||||
group = ExtResource( 2 )
|
||||
text = "45 Degree"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Isometric" type="CheckBox" parent="TopBar/ViewModeButtons"]
|
||||
margin_left = 98.0
|
||||
margin_right = 188.0
|
||||
margin_bottom = 32.0
|
||||
group = ExtResource( 2 )
|
||||
text = "Isometric"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="TopDown" type="CheckBox" parent="TopBar/ViewModeButtons"]
|
||||
margin_left = 192.0
|
||||
margin_right = 283.0
|
||||
margin_bottom = 32.0
|
||||
group = ExtResource( 2 )
|
||||
text = "Top Down"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="FrontSide" type="CheckBox" parent="TopBar/ViewModeButtons"]
|
||||
margin_left = 287.0
|
||||
margin_right = 379.0
|
||||
margin_bottom = 32.0
|
||||
group = ExtResource( 2 )
|
||||
text = "Front Side"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ObliqueY" type="CheckBox" parent="TopBar/ViewModeButtons"]
|
||||
margin_left = 383.0
|
||||
margin_right = 473.0
|
||||
margin_bottom = 32.0
|
||||
group = ExtResource( 2 )
|
||||
text = "Oblique Y"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ObliqueZ" type="CheckBox" parent="TopBar/ViewModeButtons"]
|
||||
margin_left = 477.0
|
||||
margin_right = 568.0
|
||||
margin_bottom = 32.0
|
||||
group = ExtResource( 2 )
|
||||
text = "Oblique Z"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Zoom" type="HBoxContainer" parent="TopBar"]
|
||||
margin_left = 802.0
|
||||
margin_right = 1600.0
|
||||
margin_bottom = 32.0
|
||||
size_flags_horizontal = 3
|
||||
alignment = 2
|
||||
|
||||
[node name="ZoomOut" type="Button" parent="TopBar/Zoom"]
|
||||
margin_left = 680.0
|
||||
margin_right = 710.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 30, 0 )
|
||||
text = "-"
|
||||
|
||||
[node name="ZoomPercent" type="Label" parent="TopBar/Zoom"]
|
||||
margin_left = 714.0
|
||||
margin_top = 9.0
|
||||
margin_right = 764.0
|
||||
margin_bottom = 23.0
|
||||
rect_min_size = Vector2( 50, 0 )
|
||||
text = "100%"
|
||||
align = 1
|
||||
clip_text = true
|
||||
|
||||
[node name="ZoomReset" type="Button" parent="TopBar/Zoom/ZoomPercent"]
|
||||
modulate = Color( 1, 1, 1, 0 )
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ZoomIn" type="Button" parent="TopBar/Zoom"]
|
||||
margin_left = 768.0
|
||||
margin_right = 798.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 30, 0 )
|
||||
text = "+"
|
||||
|
||||
[node name="Viewport25D" type="ColorRect" parent="."]
|
||||
margin_top = 36.0
|
||||
margin_right = 1600.0
|
||||
margin_bottom = 900.0
|
||||
rect_clip_content = true
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
color = Color( 0.301961, 0.301961, 0.301961, 1 )
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Viewport2D" type="Viewport" parent="Viewport25D"]
|
||||
size = Vector2( 1600, 864 )
|
||||
transparent_bg = true
|
||||
disable_3d = true
|
||||
usage = 1
|
||||
render_target_v_flip = true
|
||||
|
||||
[node name="ViewportOverlay" type="Viewport" parent="Viewport25D"]
|
||||
size = Vector2( 1600, 864 )
|
||||
transparent_bg = true
|
||||
disable_3d = true
|
||||
usage = 1
|
||||
render_target_v_flip = true
|
||||
|
||||
[node name="ViewportTexture" type="TextureRect" parent="Viewport25D"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
texture = SubResource( 1 )
|
||||
expand = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Overlay" type="TextureRect" parent="Viewport25D/ViewportTexture"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
texture = SubResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
[connection signal="pressed" from="TopBar/Zoom/ZoomOut" to="Viewport25D" method="_on_ZoomOut_pressed"]
|
||||
[connection signal="pressed" from="TopBar/Zoom/ZoomPercent/ZoomReset" to="Viewport25D" method="_on_ZoomReset_pressed"]
|
||||
[connection signal="pressed" from="TopBar/Zoom/ZoomIn" to="Viewport25D" method="_on_ZoomIn_pressed"]
|
||||
@@ -0,0 +1,3 @@
|
||||
[gd_resource type="ButtonGroup" format=2]
|
||||
|
||||
[resource]
|
||||
149
mono/2.5d/addons/node25d-cs/main_screen/viewport_25d.gd
Normal file
149
mono/2.5d/addons/node25d-cs/main_screen/viewport_25d.gd
Normal file
@@ -0,0 +1,149 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
var zoom_level := 0
|
||||
var is_panning = false
|
||||
var pan_center: Vector2
|
||||
var viewport_center: Vector2
|
||||
var view_mode_index := 0
|
||||
|
||||
var editor_interface: EditorInterface # Set in node25d_plugin.gd
|
||||
var moving = false
|
||||
|
||||
onready var viewport_2d = $Viewport2D
|
||||
onready var viewport_overlay = $ViewportOverlay
|
||||
onready var view_mode_button_group: ButtonGroup = $"../TopBar/ViewModeButtons/45Degree".group
|
||||
onready var zoom_label: Label = $"../TopBar/Zoom/ZoomPercent"
|
||||
onready var gizmo_25d_scene = preload("res://addons/node25d-cs/main_screen/gizmo_25d.tscn")
|
||||
|
||||
func _ready():
|
||||
# Give Godot a chance to fully load the scene. Should take two frames.
|
||||
yield(get_tree(), "idle_frame")
|
||||
yield(get_tree(), "idle_frame")
|
||||
var edited_scene_root = get_tree().edited_scene_root
|
||||
if !edited_scene_root:
|
||||
# Godot hasn't finished loading yet, so try loading the plugin again.
|
||||
editor_interface.set_plugin_enabled("node25d", false)
|
||||
editor_interface.set_plugin_enabled("node25d", true)
|
||||
return
|
||||
# Alright, we're loaded up. Now check if we have a valid world and assign it.
|
||||
var world_2d = edited_scene_root.get_viewport().world_2d
|
||||
if world_2d == get_viewport().world_2d:
|
||||
return # This is the MainScreen25D scene opened in the editor!
|
||||
viewport_2d.world_2d = world_2d
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if !editor_interface: # Something's not right... bail!
|
||||
return
|
||||
|
||||
# View mode polling.
|
||||
var view_mode_changed_this_frame = false
|
||||
var new_view_mode = view_mode_button_group.get_pressed_button().get_index()
|
||||
if view_mode_index != new_view_mode:
|
||||
view_mode_index = new_view_mode
|
||||
view_mode_changed_this_frame = true
|
||||
_recursive_change_view_mode(get_tree().edited_scene_root)
|
||||
|
||||
# Zooming.
|
||||
if Input.is_mouse_button_pressed(BUTTON_WHEEL_UP):
|
||||
zoom_level += 1
|
||||
elif Input.is_mouse_button_pressed(BUTTON_WHEEL_DOWN):
|
||||
zoom_level -= 1
|
||||
var zoom = _get_zoom_amount()
|
||||
|
||||
# Viewport size.
|
||||
var size = get_global_rect().size
|
||||
viewport_2d.size = size
|
||||
|
||||
# Viewport transform.
|
||||
var viewport_trans = Transform2D.IDENTITY
|
||||
viewport_trans.x *= zoom
|
||||
viewport_trans.y *= zoom
|
||||
viewport_trans.origin = viewport_trans.basis_xform(viewport_center) + size / 2
|
||||
viewport_2d.canvas_transform = viewport_trans
|
||||
viewport_overlay.canvas_transform = viewport_trans
|
||||
|
||||
# Delete unused gizmos.
|
||||
var selection = editor_interface.get_selection().get_selected_nodes()
|
||||
var overlay_children = viewport_overlay.get_children()
|
||||
for overlay_child in overlay_children:
|
||||
var contains = false
|
||||
for selected in selection:
|
||||
if selected == overlay_child.get("node25d") and !view_mode_changed_this_frame:
|
||||
contains = true
|
||||
if !contains:
|
||||
overlay_child.queue_free()
|
||||
|
||||
# Add new gizmos.
|
||||
for selected in selection:
|
||||
if selected.has_method("Node25DReady"):
|
||||
var new = true
|
||||
for overlay_child in overlay_children:
|
||||
if selected == overlay_child.get("node25d"):
|
||||
new = false
|
||||
if new:
|
||||
var gizmo = gizmo_25d_scene.instance()
|
||||
viewport_overlay.add_child(gizmo)
|
||||
gizmo.set("node25d", selected)
|
||||
gizmo.call("Initialize")
|
||||
|
||||
|
||||
# This only accepts input when the mouse is inside of the 2.5D viewport.
|
||||
func _gui_input(event):
|
||||
if event is InputEventMouseButton:
|
||||
if event.is_pressed():
|
||||
if event.button_index == BUTTON_WHEEL_UP:
|
||||
zoom_level += 1
|
||||
accept_event()
|
||||
elif event.button_index == BUTTON_WHEEL_DOWN:
|
||||
zoom_level -= 1
|
||||
accept_event()
|
||||
elif event.button_index == BUTTON_MIDDLE:
|
||||
is_panning = true
|
||||
pan_center = viewport_center - event.position
|
||||
accept_event()
|
||||
elif event.button_index == BUTTON_LEFT:
|
||||
var overlay_children = viewport_overlay.get_children()
|
||||
for overlay_child in overlay_children:
|
||||
overlay_child.set("wantsToMove", true)
|
||||
accept_event()
|
||||
elif event.button_index == BUTTON_MIDDLE:
|
||||
is_panning = false
|
||||
accept_event()
|
||||
elif event.button_index == BUTTON_LEFT:
|
||||
var overlay_children = viewport_overlay.get_children()
|
||||
for overlay_child in overlay_children:
|
||||
overlay_child.set("wantsToMove", false)
|
||||
accept_event()
|
||||
elif event is InputEventMouseMotion:
|
||||
if is_panning:
|
||||
viewport_center = pan_center + event.position
|
||||
accept_event()
|
||||
|
||||
|
||||
func _recursive_change_view_mode(current_node):
|
||||
if current_node.has_method("set_view_mode"):
|
||||
current_node.set_view_mode(view_mode_index) # GDScript.
|
||||
if current_node.has_method("SetViewMode"):
|
||||
current_node.call("SetViewMode", view_mode_index) # C#.
|
||||
for child in current_node.get_children():
|
||||
_recursive_change_view_mode(child)
|
||||
|
||||
|
||||
func _get_zoom_amount():
|
||||
var zoom_amount = pow(1.05476607648, zoom_level) # 13th root of 2.
|
||||
zoom_label.text = str(round(zoom_amount * 1000) / 10) + "%"
|
||||
return zoom_amount
|
||||
|
||||
|
||||
func _on_ZoomOut_pressed():
|
||||
zoom_level -= 1
|
||||
|
||||
|
||||
func _on_ZoomIn_pressed():
|
||||
zoom_level += 1
|
||||
|
||||
|
||||
func _on_ZoomReset_pressed():
|
||||
zoom_level = 0
|
||||
@@ -1,14 +1,46 @@
|
||||
tool
|
||||
extends EditorPlugin
|
||||
|
||||
const MainPanel = preload("res://addons/node25d-cs/main_screen/main_screen_25d.tscn")
|
||||
|
||||
var main_panel_instance
|
||||
|
||||
func _enter_tree():
|
||||
# When this plugin node enters tree, add the custom types
|
||||
main_panel_instance = MainPanel.instance()
|
||||
#main_panel_instance.get_child(1).set("editorInterface", get_editor_interface()) # For C#
|
||||
main_panel_instance.get_child(1).editor_interface = get_editor_interface()
|
||||
|
||||
# Add the main panel to the editor's main viewport.
|
||||
get_editor_interface().get_editor_viewport().add_child(main_panel_instance)
|
||||
|
||||
# Hide the main panel.
|
||||
make_visible(false)
|
||||
|
||||
# When this plugin node enters tree, add the custom types.
|
||||
add_custom_type("Node25D", "Node2D", preload("Node25D.cs"), preload("icons/node_25d_icon.png"))
|
||||
add_custom_type("YSort25D", "Node", preload("YSort25D.cs"), preload("icons/y_sort_25d_icon.png"))
|
||||
add_custom_type("ShadowMath25D", "KinematicBody", preload("ShadowMath25D.cs"), preload("icons/shadow_math_25d_icon.png"))
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
# When the plugin node exits the tree, remove the custom types
|
||||
main_panel_instance.queue_free()
|
||||
|
||||
# When the plugin node exits the tree, remove the custom types.
|
||||
remove_custom_type("ShadowMath25D")
|
||||
remove_custom_type("YSort25D")
|
||||
remove_custom_type("Node25D")
|
||||
|
||||
|
||||
func has_main_screen():
|
||||
return true
|
||||
|
||||
|
||||
func make_visible(visible):
|
||||
if visible:
|
||||
main_panel_instance.show()
|
||||
else:
|
||||
main_panel_instance.hide()
|
||||
|
||||
|
||||
func get_plugin_name():
|
||||
return "2.5D"
|
||||
|
||||
@@ -26,18 +26,16 @@ extents = Vector3( 5, 0.5, 5 )
|
||||
[node name="Overlay" parent="." instance=ExtResource( 3 )]
|
||||
|
||||
[node name="Player25D" parent="." instance=ExtResource( 4 )]
|
||||
position = Vector2( 0, -226.274 )
|
||||
z_index = -3952
|
||||
|
||||
[node name="Shadow25D" parent="." instance=ExtResource( 5 )]
|
||||
visible = true
|
||||
position = Vector2( 1.00261e-06, 11.2685 )
|
||||
z_index = -3958
|
||||
spatialPosition = Vector3( 3.13315e-08, -0.498, 3.13315e-08 )
|
||||
position = Vector2( 0, -226.274 )
|
||||
z_index = -3954
|
||||
|
||||
[node name="Platform0" type="Node2D" parent="."]
|
||||
position = Vector2( -256, -113.137 )
|
||||
z_index = -3954
|
||||
z_index = -3956
|
||||
script = ExtResource( 6 )
|
||||
__meta__ = {
|
||||
"_editor_icon": ExtResource( 7 )
|
||||
@@ -62,7 +60,7 @@ script = ExtResource( 9 )
|
||||
|
||||
[node name="Platform1" type="Node2D" parent="."]
|
||||
position = Vector2( -256, -339.411 )
|
||||
z_index = -3956
|
||||
z_index = -3958
|
||||
script = ExtResource( 6 )
|
||||
__meta__ = {
|
||||
"_editor_icon": ExtResource( 7 )
|
||||
|
||||
@@ -5,6 +5,7 @@ using real_t = System.Double;
|
||||
using real_t = System.Single;
|
||||
#endif
|
||||
|
||||
[Tool]
|
||||
public class PlayerSprite : Sprite
|
||||
{
|
||||
private static Texture _stand = ResourceLoader.Load<Texture>("res://assets/player/textures/stand.png");
|
||||
@@ -25,6 +26,10 @@ public class PlayerSprite : Sprite
|
||||
|
||||
public override void _Process(real_t delta)
|
||||
{
|
||||
if (Engine.EditorHint)
|
||||
{
|
||||
return; // Don't run this in the editor.
|
||||
}
|
||||
SpriteBasis();
|
||||
bool movement = CheckMovement(); // Always run to get direction, but don't always use return bool.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user