Skip to content

C# Wrapper #512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
May 8, 2025
Merged

C# Wrapper #512

merged 27 commits into from
May 8, 2025

Conversation

GabCoolDude
Copy link
Contributor

@GabCoolDude GabCoolDude commented Apr 11, 2025

Using GDScript with c# is extremely annoying, so having a C# wrapper will make things much easier. Closes #286. Based on #351.

Changes

  • Namespaces for easy use
    • PhantomCamera (Contains PhantomCamera2D, PhantomCamera3D and PhantomCameraHost)
    • PhantomCamera.Manager
    • PhantomCamera.Noise (Contains PhantomCameraNoise resources and PhantomCameraNoiseEmitters for both 2D and 3D)
  • Godot type extensions for easy converting
    • Node2D.AsPhantomCamera2D()
    • Node2D.AsPhantomCameraNoiseEmitter2D()
    • Resource.AsPhantomCameraNoise2D()
    • Node3D.AsPhantomCamera3D()
    • Node3D.AsPhantomCameraNoiseEmitter3D()
    • Resource.AsPhantomCameraNoise3D()
    • Node.AsPhantomCameraHost()
  • Add enums used by PhantomCamera
    • FollowMode
    • FollowLockAxis
    • LookAtMode
    • InteractiveUpdateMode
    • InterpolationMode
    • TransitionType
    • EaseType
    • ProjectionType
  • Add PhantomCamera classes
    • PhantomCameraTween (Resource)
    • Camera3DResource (Resource)
    • PhantomCameraNoise3D (Resource)
    • PhantomCameraNoise2D (Resource)
    • PhantomCamera (abstract class for 2d & 3d)
    • PhantomCamera3D (Node3D)
    • PhantomCameraNoiseEmitter3D (Node3D)
    • PhantomCamera2D (Node2D
    • PhantomCameraNoiseEmitter2D (Node2D)
    • PhantomCameraHost (Node)
    • PhantomCameraManager (static class)
    • ActivePhantomCameraQueryResult
      • This is a helper class used when calling PhantomCameraManager.GetActivePhantomCamera() which may be null, 2d, or 3d
    • LimitTargetQueryResult
      • This is a helper when calling PhantomCamera2D.GetLimitTarget() which may be null, TileMap, TileMapLayer or CollisionShape2D

All underlying object types can be reference by calling the node type field:

var pcam = GetNode<Node3D>("path/to/Node3D").AsPhantomCamera3d();
pcam.Node3D.GlobalPosition = new Vector3(0, 0, 0); // access the underlying Node3D

Each class also follows the Godot convention of having a MethodName field with all the string constants for the gdscript getters and setters. PhantomCamera contains the shared strings, PhantomCamera2D and PhantomCamera3D contain their own unique strings.

sircodemane and others added 10 commits July 17, 2024 00:00
- Begin effort on PCam3D and some basic Godot extensions and PCam types
- added Engine singleton registration to PhantomCameraManager
- fixed incorrect LookAtMode signal (not available in 2d)
- added c# project files
- created basic test runner and added some tests (wip)
- fix several bugs from invalid getter/setters, properties, and types
- added more tests
- added LimitTarget query result type for working with TileMaps and CollisionShape2Ds
- reorganized scripts since main script file had become quite large
- added missing shared camera properties
- added missing phantom camera 2d properties
- added missing phantom camera 3d properties
- Begin effort on PCam3D and some basic Godot extensions and PCam types
@GabCoolDude
Copy link
Contributor Author

GabCoolDude commented Apr 11, 2025

Unfortunately the merge didn't go as I planned, so a LOT of the UIDs changed (just look at the files changed count, it went from 25 to 61), I don't know if that might be a problem though. I also did some changes like adding PhantomCameraNoise3D and PhantomCameraNoiseEmitter3D, more coming soon.

I hate merging manually
@sircodemane
Copy link
Contributor

🙌 this is awesome to see, thank you so much for picking this up @GabCoolDude

@GabCoolDude
Copy link
Contributor Author

If a variable has multiple setters, should I add them all, or just one ? Example:

image

image

@sircodemane
Copy link
Contributor

If a variable has multiple setters, should I add them all, or just one ? Example:

@GabCoolDude I don't have a good answer for this. I think for safety it would be appropriate to offer whatever is supported by the godot objects so the interfaces match closely. One small C# specific improvement you could probably add is just having one AppendFollowTargets() method that takes a variadic number of targets. I say just follow your heart, but @ramokz may have a different opinion lol

@GabCoolDude GabCoolDude mentioned this pull request Apr 11, 2025
@ramokz
Copy link
Owner

ramokz commented Apr 12, 2025

If a variable has multiple setters, should I add them all, or just one ?

Yes, all public functions should be included. So if it exists in the GDScript version / documentation site, the C# wrapper should also include them. Otherwise, we end up with method availability discrepancies between GDScript and C# users.

A minor note in the C# snippet above, the "s" in "Targets" is important.
Basically, follow_target and follow_targets are two separate properties. The former is Node2D/3D and the latter is an Array[2D/3D]. E.g. EraseFollowTarget() might imply you're erasing the follow_target, but it's actually removing a node from follow_targets array.

One small C# specific improvement you could probably add is just having one AppendFollowTargets() method that takes a variadic number of targets.

Are you thinking of combining AppendFollowTargets and AppendFollowTargetsArray into a function overload? Both functions exist on in the GDScript, which doesn't support overloading, variant, where each takes a different parameter, Node vs. Array[Node]. Think keeping them separate keeps documentation discrepancies to a minimum?

@ramokz
Copy link
Owner

ramokz commented Apr 12, 2025

As an aside, think the Unit test bit should be pulled out of this PR and into a separate one.

Ideally, want to keep PRs as specific as possible, so it doesn't take longer than necessary to get merged and split the review process into smaller, more specific, chunks.

@GabCoolDude
Copy link
Contributor Author

GabCoolDude commented Apr 12, 2025

As an aside, think the Unit test bit should be pulled out of this PR and into a separate one.

Ideally, want to keep PRs as specific as possible, so it doesn't take longer than necessary to get merged and split the review process into smaller, more specific, chunks.

I agree, especially since I am having a bit of trouble with it currently. Will be removed in the next commit.

@GabCoolDude
Copy link
Contributor Author

Should I put doc comments on each variable and class, just like in GDScript, or should I leave them be ? I think they'd be quite useful, because you wouldn't have to pull up the documentation in a browser tab and would just hover over the property to see what it does.

@GabCoolDude GabCoolDude marked this pull request as draft April 15, 2025 09:42
@ramokz
Copy link
Owner

ramokz commented Apr 15, 2025

Should I put doc comments on each variable and class, just like in GDScript, or should I leave them be ? I think they'd be quite useful, because you wouldn't have to pull up the documentation in a browser tab and would just hover over the property to see what it does.

From a DX perspective, it makes total sense.

However, from a maintainability perspective, I am reluctant to add it simply because of the extra work to keep descriptions in sync across the various places. Think it's one of those things where once you start doing it, you have to keep doing it.
It is sadly already getting cumbersome with keeping the GDScripts and the documentation site aligned whenever typos are found or other changes are needed. So I am cautious about adding more work to keep track of and maintain than absolutely necessary.

You can also access the documentation from Godot directly by looking it up by using Help > Search Help... (shortcut F1) and searching for the specific addon class. Not as elegant as seeing it in the IDE, but it's a faster way to look up things without an internet connection or having to open the site.

@GabCoolDude
Copy link
Contributor Author

Turns out that the method we had before was the best way to go about it without creating C# bindings using C++. I had a little conversation in the Godot Engine discord related to an error I had, which would've caused the whole C# wrapper to not work with GetNode<>() and As<>(), and it turns out that there is no way to fix it because Godot will recognize the result from GetNode() as a Node and type casting it to PhantomCamera3D or any other type will just cause an error, and that the method we had before was actually the best method. I will now be reverting the previous commit.

@GabCoolDude GabCoolDude marked this pull request as ready for review April 15, 2025 14:10
@sircodemane
Copy link
Contributor

Turns out that the method we had before was the best way to go about it without creating C# bindings using C++. I had a little conversation in the Godot Engine discord related to an error I had, which would've caused the whole C# wrapper to not work with GetNode<>() and As<>(), and it turns out that there is no way to fix it because Godot will recognize the result from GetNode() as a Node and type casting it to PhantomCamera3D or any other type will just cause an error, and that the method we had before was actually the best method. I will now be reverting the previous commit.

Sorry I wasn't able to provide this insight sooner. I saw your question about this last night and I could not recall the exact reason I chose that method. I thought that it was a DX choice because it also allowed the ability to create the C# extension methods. But I think I remember having issues trying the As<>() method too. As for not extending the base Node/Node3D classes, I don't remember the exact reasoning, but I believe there was another technical limitation there as well, but it may also have been a personal preference of composition instead of inheritance.

Some of the choices I made were also because I was looking at other popular plugins to see their approach to adding C# support. I think DialogueManager was one I took a lot of inspiration from. Now that you've spent some time with the code, it might be worth looking through the conversations on the old PR again to see if there are any useful insights there.

Also, nice work on this!

@ramokz
Copy link
Owner

ramokz commented Apr 16, 2025

That's a good discovery! Thanks for looking into it. Yeah, let's go with what the previous iteration was doing.
I will make a note to document it on the site post PR merge.

Copy link
Owner

@ramokz ramokz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a high-level review of the PR, have yet to dig into the various class files.

Still trying to get a custom build of Godot mono working to test various things, but it is proving more cumbersome than I had hoped.

- Removed dedicated dev scene for C# (and its script)
- Removed junk files from the .csproj file
- Fixed dev scene 3D having the wrong root node
@GabCoolDude
Copy link
Contributor Author

Still trying to get a custom build of Godot mono working to test various things, but it is proving more cumbersome than I had hoped.

You can just download the .NET version from the website. If you use the Steam version of Godot, you can still use it with this method.

@ramokz
Copy link
Owner

ramokz commented Apr 20, 2025

You can just download the .NET version from the website. If you use the Steam version of Godot, you can still use it with this method.

Thanks! But was referring to compiling a mono build of the Godot engine itself. It might just be more complicated due to my OS environment not playing nicely with mono / dotnet. At the minute, the custom build thinks I'm using an outdated C# version, which doesn't really make sense… It works fine with a typical Godot mono executable, so it's likely a build process step on my part.

For a bit of context, the reason I need to compile the engine locally is to test a few custom export templates that most users will not need, but those who do will raise an issue about it. The reason it matters is that it might mean the way the C# classes are currently set up would need a structural change.

- Removed ReSharper comment in PhantomCamera2D.cs
- Added getters in PhantomCameraManager.cs
- Removed the android and ios specific parts of the csproj
(idk why it was changed, it wasnt me, i just tried to fix it)
@ramokz
Copy link
Owner

ramokz commented Apr 29, 2025

Managed to figure out what the issue was and got around to testing the custom export templates.
It all seems to run fine without any issues, so structurally it should all be sound 🎉

Will do a sweep of all the properties and methods next to make sure all are accounted for, so there's no functionality discrepancies between this and GDScript.

Copy link
Owner

@ramokz ramokz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got through all the properties and methods of the various GD classes and compared them with the C# classes in this PR. Most of it checks out, and only spotted a few relatively minor things.

After those changes are made, I think we can merge this into a beta branch and to share publicly to get any feedback before merging into main. Mainly to avoid making potential breaking changes right after release. Given the size and complexity of this PR, I would want to make sure we get it as right as we can.

…views

- Added KeepAspect property and enum in Camera3DResource.cs
- Removed PhantomCameraHostOwner property in  PhantomCamera.cs
- Added TeleportPosition to PhantomCamera2D.cs
- Replace Rotation properties with setters and getters in PhantomCamera3D.cs
- Added missing methods in PhantomCameraNoiseEmitter2D.cs and PhantomCameraNoiseEmitter3D.cs
- Made enum types match their GDScript counter-part in PhantomCameraTween.cs
- Removed "Rotation" from GetThirdPersonQuaternion and its setter
- Gave GetThirdPersonQuaternion and its setter the correct MethodName
- Added LimitTarget property in PhantomCamera2D.cs
- Removed constant floats from Camera3DResource.cs
@ramokz ramokz dismissed a stale review May 5, 2025 14:06

Account doesn't exist, possibly a bot

@ramokz ramokz changed the base branch from main to csharp-wrapper May 7, 2025 00:15
@ramokz
Copy link
Owner

ramokz commented May 8, 2025

Looks good! Will merge this into a beta branch for people to test out and provide feedback before adding this to a proper public release.

Really appreciate the effort getting this working @sircodemane @GabCoolDude!

@ramokz ramokz merged commit b1de927 into ramokz:csharp-wrapper May 8, 2025
@GabCoolDude GabCoolDude deleted the csharp-wrapper-new branch May 13, 2025 10:39
ramokz added a commit that referenced this pull request May 26, 2025
* C# Wrapper (#512)

* WIP: Adding CSharp wrapper
- Begin effort on PCam3D and some basic Godot extensions and PCam types

* WIP: rough first pass at PhantomCamera with 2D and 3D classes

* (#286) Finished the C# API

* WIP #286: fixed bugs in c# wrapper and added tests
- added Engine singleton registration to PhantomCameraManager
- fixed incorrect LookAtMode signal (not available in 2d)
- added c# project files
- created basic test runner and added some tests (wip)

* WIP #286: more tests and re-organized
- fix several bugs from invalid getter/setters, properties, and types
- added more tests
- added LimitTarget query result type for working with TileMaps and CollisionShape2Ds
- reorganized scripts since main script file had become quite large

* wip: #286 C# Wrapper
- added missing shared camera properties
- added missing phantom camera 2d properties
- added missing phantom camera 3d properties

* WIP #268: catching up to release 0.7.3

* WIP: Adding CSharp wrapper
- Begin effort on PCam3D and some basic Godot extensions and PCam types

* Fix addon not working correctly

I hate merging manually

* Implement PCamNoiseEmitter2D and PCamNoise2D

* Add more tests, still unfinished

* Address reviews

* Finalize c# wrapper logic

Added missing setters: if a variable had multiple setters, they were missing, so they were added.

* Make all classes variant compatible

* Revert "Make all classes variant compatible"

This reverts commit 902b2f1.

* Partly address reviews

- Removed dedicated dev scene for C# (and its script)
- Removed junk files from the .csproj file
- Fixed dev scene 3D having the wrong root node

* Address remaining reviews

- Removed ReSharper comment in PhantomCamera2D.cs
- Added getters in PhantomCameraManager.cs
- Removed the android and ios specific parts of the csproj

* Remove left over doc comment

* Revert dev_scene_3d.tscn to how it was before

(idk why it was changed, it wasnt me, i just tried to fix it)

* Add missing properties and methods from recent updates and address reviews

- Added KeepAspect property and enum in Camera3DResource.cs
- Removed PhantomCameraHostOwner property in  PhantomCamera.cs
- Added TeleportPosition to PhantomCamera2D.cs
- Replace Rotation properties with setters and getters in PhantomCamera3D.cs
- Added missing methods in PhantomCameraNoiseEmitter2D.cs and PhantomCameraNoiseEmitter3D.cs
- Made enum types match their GDScript counter-part in PhantomCameraTween.cs

* Address reviews

- Removed "Rotation" from GetThirdPersonQuaternion and its setter
- Gave GetThirdPersonQuaternion and its setter the correct MethodName
- Added LimitTarget property in PhantomCamera2D.cs
- Removed constant floats from Camera3DResource.cs

---------

Co-authored-by: Cody Bentley <[email protected]>

* Rotation fix (#534)

* Reverted UIDs

* Indentation formatting

* Added new getter functions

* Renamed function is_looking_at() to is_looking()

* Additional formatted file

* Fixed issue Array setters not working

* Removed FollowTargetPosition and LookAtTargetPosition getters

---------

Co-authored-by: GabCoolGuy <[email protected]>
Co-authored-by: Cody Bentley <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

C# wrapper
3 participants