godot-nim icon indicating copy to clipboard operation
godot-nim copied to clipboard

Example request

Open survivorm opened this issue 6 years ago • 55 comments

Can you elaborate an example like code in GDScript and same code in godot-nim. I have a vague idea, how it would look like, but not quite sure i'm right. Of cource I may apply try and see approach, but i'm quite positive on that i'm not the only one with such problems. For example: From Jaynam tutorial https://www.youtube.com/watch?v=kc-zJnRvPUY

extends Camera

export var distance = 12.0
export var height = 4.0

func _ready():
	# Called every time the node is added to the scene.
	# Initialization here
	set_physics_process(true)
	set_as_toplevel(true)  # Independant from the parent position


func _physics_process(delta):
	var target_position = get_parent().get_global_transform().origin
	var camera_position = get_global_transform().origin
	var up = Vector3(0, 1, 0)

	var camera_offset = camera_position - target_position

	camera_offset = camera_offset.normalized()*distance
	camera_offset.y = height

	camera_position = target_position + camera_offset

	look_at_from_position(camera_position, target_position, up)

will be transformed to

some code

And also you need to add script, choose NativeScript, choose class name from nim code, and (some other things)


This things are pretty simple, i'm almost sure it all revolves around gdobj macro, but there are some unclear points. Like name translation rules (CamelCase to underscore_delimited and back), using inherited procs (like look_at_from_position in this ex.), using methods of GD core objects (like Input.is_action_pressed) and so on. Then do i need to write custom toVariant/fromVariant/godotTypeInfo and then it may be crated automatically (and is there any such case)? All of this questions may be solved by simple but throughout example. Step-by-Step, from binding to the editor object till running scene. This may be a little boring for you, but it will greatly help people who start their very first godot+nim project.

survivorm avatar Mar 22 '18 08:03 survivorm

Found scripts in stub project doing in essence what what i'd asked. Beg you pardon on my sillyness, but docs are lacking info on there to look for nim godot scripts and on how it's tailored together (I mean i've found them, but it would be for the better to add something like: "Nim godot scripts sources can be found in /src doc. /src/stub.nim is considered the root of the project, it's compilation is added in nakefile.nim" to the readme of the stub project)

survivorm avatar Mar 23 '18 13:03 survivorm

I would also love some more info on how the stub is structured and the workflow of Nim and Godot ala the readme of godot-go: https://github.com/ShadowApex/godot-go

zetashift avatar Mar 27 '18 13:03 zetashift

Also, some parts of how the API works are a little hard to catch at a glance: for example, as NodeType constructs like findNode("Stop") as BaseButton They can be found in stub, yes, but a little inline comments would be welcome

survivorm avatar Apr 04 '18 08:04 survivorm

And, of course, the most welcome addition will be a simple mapping like godot Class/method -> nim class/method (with module path) would be MOST WELCOME. Again: MOST WELCOME

survivorm avatar Apr 04 '18 08:04 survivorm

I'm planning to make a very simple open source example game based on godot-nim to show how it can be used. Not sure when I will get to that though.

The mapping from GDScript to Nim is straightforward: modules are just class names in snake_case (TextureRect -> texture_rect.nim), methods are in lowerCamelCase (remove_child -> removeChild). If you use VSCode and it is correctly configured, you should get auto-complete suggestions. Also, the generated modules have all method declarations at the top so you can quickly observe what it contains. For example, if you would like to know what you can do with TextureRect, just open texture_rect.nim and you'll see at the top:

const
  STRETCH_SCALE_ON_EXPAND* = 0'i64
  STRETCH_SCALE* = 1'i64
  STRETCH_TILE* = 2'i64
  STRETCH_KEEP* = 3'i64
  STRETCH_KEEP_CENTERED* = 4'i64
  STRETCH_KEEP_ASPECT* = 5'i64
  STRETCH_KEEP_ASPECT_CENTERED* = 6'i64
  STRETCH_KEEP_ASPECT_COVERED* = 7'i64
proc texture*(self: TextureRect): Texture {.gcsafe, locks: 0.}
proc `texture=`*(self: TextureRect; val: Texture) {.gcsafe, locks: 0.}
proc expand*(self: TextureRect): bool {.gcsafe, locks: 0.}
proc `expand=`*(self: TextureRect; val: bool) {.gcsafe, locks: 0.}
proc stretchMode*(self: TextureRect): int64 {.gcsafe, locks: 0.}
proc `stretchMode=`*(self: TextureRect; val: int64) {.gcsafe, locks: 0.}

endragor avatar Apr 04 '18 09:04 endragor

That's great and all. But either you do it or i'll eventually try to build authomatic doc on top of godot's class descriptions. Like TextureButton methods Nim source package: texture_button

GoDot Nim
is_pressed() pressed()
... ...

TextureButton properties

GoDot Nim
disabled disabled
... ...

survivorm avatar Apr 04 '18 10:04 survivorm

"pressed" is a property and so "is_pressed" and "set_pressed" are not generated to enforce consistent usage and brevity. button.pressed works both in GDScript and Nim.

endragor avatar Apr 04 '18 12:04 endragor

That's a complicated question. pressed() - is not a property, though still named the same. And if GD policy for accessors is is_{key} it shoud be mapped as is{Key} to nim, IMO. Otherwise it IS NOT consistent. And direct property usage in case of having accessors/mutators is an incapsulation breach. And should be discouraged, IMHO. But that's discussible. Did you seen the other issue i've created? I think it's one of the things you may encounter trying to oversimplify api behaviour.

survivorm avatar Apr 04 '18 13:04 survivorm

How did you derive it's not a property? If there are pressed and pressed= procedures generated, it means Godot reported it as a property. And you can see pressed is listed as a member variable (property) here: http://docs.godotengine.org/en/3.0/classes/class_basebutton.html

Yes, I saw the issue, and that problem would not be avoided by adding is_pressed and set_pressed, because pressed and pressed= would still be there and would conflict with the method pressed.

GDScript allows this:

button.pressed
button.is_pressed() # equivalent of above
button.pressed = true
button.set_pressed(true) # equivalent of above

Nim just removes is_pressed() and set_pressed(). The name clash problem appears because, unlike in GDScript, there is no separate concept of properties in Nim and properties are simulated with two procedures. So they may clash with other procedures in the same class. methods are inteded for overriding behaviour, so the solution could be to add a suffix in case of a clash, such as Impl, so that method pressed would turn into method pressedImpl.

endragor avatar Apr 04 '18 15:04 endragor

I think the metod/proc clash resolution strategy you've given is right. Might be other suffix/or even prefix, but that's insignificant details. About class properties and accessors. Yes, GDScript do not impose you to use accessors, it's not strict. But even it supplies them. Nim is much more strict, and though you "throw the accessors away" I think it's a wrong way to do it. After all, i don't think OOP's accessor/mutator concept useless. Class properties should be considered private outside of the class. And you should have accessors/mutators (or only one of them) for accessing them from the outside. I think it's a sane idea, and the way you give looks more like a lazy way of doing things (no offence, i'm grateful for the godot-nim's existance, i just want it to be even better). Anyway - it's for you to decide, i may only explain my position.

survivorm avatar Apr 05 '18 06:04 survivorm

I don't throw the accessors away, it's all about syntax. proc pressed and proc pressed= are a getter and a setter, working exactly the same as proc is_pressed and proc set_pressed would. So I don't see how removing the latter two affects anything, aside from enforcing consistency of your codebase (you cannot use is_pressed in one place and pressed in another).

endragor avatar Apr 05 '18 08:04 endragor

The point is why accessor was named is_pressed in the first place. It's not about the inability to make a poc with pressed() name, it's more about distinguishing. It's a philosophical question, but I prefer to see that I do explicitly. Yes, i'm setting internal class property, but i'm using mutator for that. I'm SETTING it. Imagine

proc `pressed=`

got lost somethere on the way. Now, using your code scheme, you're not using mutator any more. And you still think you are. And what if, for example, mutator had some check inside, and was able to throw an exception? No check for you, no idea it is not made. After all, that's the point - cleaner and more explicit code is more predictable. (Yeah, i'm python's fan, don't blame me:))

survivorm avatar Apr 05 '18 09:04 survivorm

Though you speaking of a more JS's kind of thinking. If there's 3 ways to do a thing, it doesn't mean all of them are correct ones :)

survivorm avatar Apr 05 '18 09:04 survivorm

I still don't get your point. I'm avoiding 2 ways to do the exactly same thing in the first place, I don't see where there is "JS thinking" in that.

Now, using your code scheme, you're not using mutator any more. And you still think you are. And what if, for example, mutator had some check inside, and was able to throw an exception? No check for you, no idea it is not made.

It will be made if it's there. There is ZERO difference between pressed and is_pressed and GDScript allows both, and both work the same in every way.

And if pressed= "gets lost", your code just stops compiling, because there is no such field in the type definition.

endragor avatar Apr 05 '18 09:04 endragor

Okay, your way to go. So, you don't have properties on the nim's side at all? It's a little confusing, but viable. A minor problem is that you effectually "mask" the things you shoudn't. Cause nim's

x.pressed = true

is not similar to

x.pressed = True

it's effectually

x.set_pressed(True)

And i really don't see why are you so insistent on doing so. It's just confusing. My point is - you may suppress the x.pressed behaviour at all as it's non-OOP in godot. But the godot's x.set_pressed is optimally translated to x.setPressed as it's a way to go with the other functions. Why change that, why make special case for accessors/mutators. First of all - they are FUNCTIONS. Anyway, i'd be happy with any decision, i just don't see a point in your logic.

survivorm avatar Apr 05 '18 09:04 survivorm

It seems you don't realize that Godot's x.pressed = true executes exactly as x.set_pressed(true), and so does Nim's x.pressed = true. The concept of properties with custom getters/setters does exist in OOP languages (C# has it, for instance).

So what we are talking about here is selecting between:

Option 1. Add duplicates:

proc pressed*(self: BaseButton): bool
proc is_pressed*(self: BaseButton): bool
  ## Does the exact same thing as above, including any checks or side effects or anything.

proc `pressed=`*(self: BaseButton; val: bool)
proc set_pressed*(self: BaseButton; val: bool)
  ## Does the exact same thing as above, including any checks or side effects or anything.

Option 2. Only property-like procedures, this is implemented now:

proc pressed*(self: BaseButton): bool
proc `pressed=`*(self: BaseButton; val: bool)

Option 3. Only get/set procedures:

proc is_pressed*(self: BaseButton): bool
proc set_pressed*(self: BaseButton; val: bool)

I went with option 2 because: a) Unlike option 1, it enforces consistency of user codebase. b) It's more concise than option 3.

endragor avatar Apr 05 '18 10:04 endragor

Looks like you're right, i didn't realise that in godot you may access/modify your property only via accessors/mutators. My bad. As of the other things... I think option 1 is off the table, because duplicates are rarely needed and that's certainly not the case. So distinguish is between 2 and 3. I better like 3, because it's more explicit and can't be mixed up with anything else, but that's really a matter of taste. So, i'll be happy if you'll fix the proc/method clash and that's all. And the 'transition' table, godot-to-nim, of course. But that one may wait :) In my defence, i may say it's rather hard to imagine that property direct access and access via mutators is essentially the same. And i think i'm now one of only the few of knowing it works that way in GoDot

survivorm avatar Apr 05 '18 10:04 survivorm

@survivorm I know the conversation moved on passed this a bit, but I translated that script for you. I did it exactly as-is (with the exception of moving the UP vector out to show you this is still nim, and can benefit from nim features).

By the way you don't need set_physics_process(true) in godot-nim nor in gdscript... I think people still do that as a remnant from Godot 2.x which just spreads misinformation, but I digress.

import
  godot,
  camera,
  spatial

# I like putting these kinds of things in another file and importing them
const UP* = vec3(0, 1, 0)

gdobj CameraFollow of Camera:
  var distance {.gdExport.} = 12.0
  var height {.gdExport.} = 4.0

  method ready() =
    setAsToplevel(true)

  method physicsProcess(dt: float) =
    # I like to store these, already cast, as member variables so I don't need to cast on-demand
    let targetPosition = (getParent() as Spatial).globalTransform.origin
    var cameraPosition = self.globalTransform.origin
    var cameraOffset = cameraPosition - targetPosition

    cameraOffset = cameraOffset.normalized * distance
    cameraOffset.y = height

    cameraPosition = targetPosition + cameraOffset

    lookAtFromPosition(cameraPosition, targetPosition, UP)

rosshadden avatar Apr 24 '18 02:04 rosshadden

Thanks. Great example, maybe @endragor will include it into the doc. It's very illustrative, IMO Though i'm gone a little forward, it'll help someone else

survivorm avatar Apr 25 '18 12:04 survivorm

It also only took around five minutes, which shows how easy it is to adapt code between them once you are familiar enough with gdscript, nim, and godot-nim.

Also worth noting in nim you can write things like getParent as get_parent. I didn't do that because I think snake_case is absolutely hideous, but if I had it would look a lot more like the original source in gdscript.

rosshadden avatar Apr 25 '18 17:04 rosshadden

Yeah, great. Though i don't think nim gd scripts should be written in the gd style, better in nim's. Though - it's a point of view matter (what do you consider primary - GD or Nim). Sadly, ambiguity issue isn't closed yet

survivorm avatar Apr 26 '18 07:04 survivorm

Hello community

I'm thinking of using Nim in Godot, but I have many doubts that are not clear to me.

The fact that there are no complete examples of code with a couple of simple games increase my doubts.

I also have doubts that the time I dedicate not worth it for the time necessary for their learning.

Certainly I could investigate by reading all the documentation, but this already takes a lot of time.

Currently I know C # and JavaScript very well, I earn a living with them by professionally programming.

My doubts are with respect to the necessary time regarding C #.

I am new to Godot. It took me a week, about 40 hours to learn it for a normal start a project, without being an expert in it.

I have seen GDScript, it is very easy to learn, but insufficient.

Now I was thinking about moving to C# with Godot, I imagine that handling C # with Godot will be another 40 hours.

I am aware that answering the question I am going to ask is difficult, but considering a normal intelligence.

How long can I take to learn Nim to an appropriate degree?

How long can I take to learn Nim with Godot?

I know of the best performance of Nim's language, also of its nice syntax, but I am afraid of its reduced community and if the necessary time will compensate for the project I want to do.

Sorry for my English, I defend reading but I write it very badly

Thank you

VictorGonzalezFernandez avatar Jun 20 '18 12:06 VictorGonzalezFernandez

Hello. About learning the Nim - it depends on your background. I like to think of Nim as a Python-like syntax with strict type system. It's rather crude, yes, but it gives you a start. First of all, you're easier to learn Nim as much you're familiar with python-like syntax and with strict typed languages. Nim's rather easy to learn, you may start small and then discover features for you. Also, Nim has great community. It's rather small, but open and always ready to answer. You may just go to the IRC (gitter/matrix) channel or to TG group and just ask whatever you like, even post your program sample, and get tips on what you're doing wrong and/or what you need to do to achieve your goal. Don't be shy and ask whatever noob question you like. With godot itself... It's a bit more complicated. If you know how to do someth. in GDScript, you're likely easily implement it in Nim-godot. It's simple, really. And translation rules are quite clear. But if you're stuck... It's a little more complicated. You may ask in channel, but if question is godot-specific it's likely people here wouldn't be able to help. Not saying they wouldn't try. You may ask here and you'll get an answer. Eventually. But that means a duration between hours and days. Nim-godot society is quite specific and rather small. That's only my opinion and result may differ. But i advise you to take a shot on it. Go on and try Nim :)

survivorm avatar Jun 21 '18 08:06 survivorm

Also, try asking nim beginner's guide in the society, depending on your background. People will give you links on the info best suits your level/background

survivorm avatar Jun 21 '18 08:06 survivorm

Thanks for your answer.

I currently know that with C # in a week I am running Godot. I know C # from several years of experience. I am afraid that with Nim he does not master him well in two months of study. I have doubts that the advantages are so many to compensate for the learning time, especially if the community is so small.

You have encouraged me, which I sincerely appreciate, but I still have the doubt. ☹

VictorGonzalezFernandez avatar Jun 21 '18 08:06 VictorGonzalezFernandez

Nim needs examples of small games in Godot as life needs water, a must.

VictorGonzalezFernandez avatar Jun 21 '18 08:06 VictorGonzalezFernandez

Your choice. I think you're right about the examples, but with 1-2 people maintaining this project this will unlikely happen soon. On practical side, if you've experience with C#, it might be the best decision to take time-wise. But I'll be happy if you'll join the Nim community in your free time. It may be fun for you too.

survivorm avatar Jun 21 '18 09:06 survivorm

Ok I'll try Nim. I have seen that there is a book. I suppose one way to achieve extended support is to donate a small amount per month, for example 10 or more. No abuse, but to respond to questions not found in the documentation

VictorGonzalezFernandez avatar Jun 21 '18 12:06 VictorGonzalezFernandez

The book (Nim in Action) is a really good resource for learning. I read it after I had already learned Nim, but it was still a good read.

rosshadden avatar Jun 21 '18 15:06 rosshadden

Im also new, only a few weeks into godot-nim and nim, learning both at the same time. More than often not the easiest task, but nevertheless fun! ;) I find the basics of Nim easy to learn , but struggle to go turbo on it. Right now im using it like GDScript, because i have problems understanding when to make use of generics, templates and macros. But i think it is worth to dig into Nim, for me it's GDScript on steroids. Anyway, for some reason im not getting answers on discord, so i will try it here, don't want to open a new issue only to ask some simple questions.

  1. How to Nim Input.is_action_pressed? i can only use event.* but i also need Input.*
  2. How to use Signals? esp. connect()
  3. How to Nim call_deferred("add_child, scene") ?

rama0x9 avatar Jun 21 '18 17:06 rama0x9