Skip to content
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

Allow ability to override parent variables #338

Open
Feniks-Gaming opened this issue Dec 27, 2019 · 23 comments · May be fixed by godotengine/godot#93787
Open

Allow ability to override parent variables #338

Feniks-Gaming opened this issue Dec 27, 2019 · 23 comments · May be fixed by godotengine/godot#93787

Comments

@Feniks-Gaming
Copy link

Describe the project you are working on:

Arcanoid Clone for learning

Describe the problem or limitation you are having in your project:

I have a scene BrickParent that has behaviours for all my bricks in a game. I then want multiple inherited scenes of that scene to be different colour bricks. Each brick is worth different amount of points in the game

In my BrickParent scene I have var points = 0 I then want this var points to be different in each inherited scene. So In my inherited scene I created a inherited script that inherits from BrickParent.gd if I type var points = 50 U will get error Member 'points' already exists in parent class. I would like to be able to override this just how I can override some of the functions in parent class that already exist.

Describe how this feature / enhancement will help you overcome this problem or limitation:

This will will make code easier to follow across different inherited scenes. And allow for faste rprototyping

Show a mock up screenshots/video or a flow diagram explaining how your proposal will work:

N/A

Describe implementation detail for your proposal (in code), if possible:

I can't help here I have no idea how to do it.

If this enhancement will not be used often, can it be worked around with a few lines of script?:

I would expect it to be used every time people create inherited scripts. Work around for now is to add all the variables we want to change in _ready() function it works but for example you can't override constants in that way.

Is there a reason why this should be core and not an add-on in the asset library?:

It's pretty much part of the way core

@Calinou
Copy link
Member

Calinou commented Dec 27, 2019

I believe you could implement something similar by overriding a method that returns a constant value. I think it's done this way in some other programming languages.

@KoBeWi
Copy link
Member

KoBeWi commented Dec 28, 2019

You can just export the variable if your bricks are scenes. Or overwrite it in _ready().

@dalexeev
Copy link
Member

dalexeev commented Dec 28, 2019

This may actually be helpful!

For example, if a script inherits from the Node class, then it can be added to the scene. But at the same time, the inherited properties will have default values from the base class. It would be nice to be able to override default values something like this:

extends RichTextLabel
class_name TypeWriter

default rect_size = Vector2(128, 64) # only constant values
default bbcode_enabled = true
default visible_characters = 0
default scroll_active = false
default scroll_following = true

# ...

(I know that you can use the tool keyword.)

ADD. I mean that in the editor (if you do not use the tool keyword), the inherited properties by default have some values, and in _ready() they will be assigned different values.

@Feniks-Gaming
Copy link
Author

@KoBeWi I know I can but this doesn't solve my problem as exactly as I would like it to be.

For example say I have a scene Bullet it has const SPEED = 400 I want it to be always 400 on run time in parent. But I would like the SPEED to be always 800 in it's child FastBullet I can't change it in _ready() function because you can't change constants

However I don't want to change constant to var. Because it will no longer act as constant during runtime. Even tho this is exactly what I want it to do.

Ability to override variables rather than just change them will solve situations like this.

@KoBeWi
Copy link
Member

KoBeWi commented Dec 28, 2019

Ability to override variables rather than just change them will solve situations like this.

But still, constants aren't variables.

Also, what's the problem with changing const to var? Keeping them UPPERCASE should be enough, there aren't any performance differences.

@DleanJeans
Copy link

You can have a multiplier variable, defaulted to 1. Then just use BASE_SPEED * multiplier in _physics_process and change multiplier in inherited scenes. Say multiplier = 2 in your FastBullet.

@Feniks-Gaming
Copy link
Author

Difference is that is I accidentally change constantly SPEED somewhere in a game I will get an error knowing that it was a mistake I intended to avoid if I call my var SPEED I can change it by accident and not know about it.

@Calinou
Copy link
Member

Calinou commented Dec 29, 2019

@Feniks-Gaming Would godotengine/godot#23695 solve this particular use case?

@Feniks-Gaming
Copy link
Author

Yes that would solve my issue. I would be able to create a parent with empty variable and let SPEED and then assign it on other children like to whatever I want and it would act like a constant otherwise.

@vnen
Copy link
Member

vnen commented Jan 17, 2020

Problem with this is that it allows for potential mistakes on the other end (when you override the parent variable without realizing). Maybe with ahem annotations we could have a tag to allow an explicit override:

@override
var name = "New Node Name"

Can be a keyword but I don't want to introduce yet another one.

Still, doing that for constants may not be possible. After all, they are supposed to be constants, and they are (or should be) optimized based on this assumption.

@ModProg
Copy link

ModProg commented Jun 20, 2021

I would like to bring another point that this would enable to provide different defaults for custom SubClasses from Standard Nodes. Similar to the ToolButton when you open the Documentation it actually says [override: true] for the flat property:

grafik

This is something currently impossible to do via GDScript, because when you change a default via a tool script (in _init or _ready etc.) you wont be able to change the default in the Inspector afterwards, because it will always reset to the value set in script on reloading the scene.

@dalexeev

This comment was marked as resolved.

@Calinou Calinou changed the title Allow ability to overwrite parent variables Allow ability to override parent variables Apr 27, 2022
@CharityMoor
Copy link

CharityMoor commented Oct 1, 2022

I also run into this issue a lot when working with inherited classes. Using _init() works well enough for changing the default values of non-exported variables, but if there is a way to make it work with exported variables it seems like it would need to involve @tool scripts, which themselves can interact unintuitively with the inheritance structure, and require a lot more care throughout the script to make sure only the right code is being executed in the editor. Assuming that one of the main advantages of inheritance is to avoid duplicating work, and the main advantage of changing a default value is the same, implementing a workaround that involves duplicating work and opening code to difficult-to-foresee errors seems undesirable.

Problem with this is that it allows for potential mistakes on the other end (when you override the parent variable without realizing). Maybe with ahem annotations we could have a tag to allow an explicit override:

@override
var name = "New Node Name"

Can be a keyword but I don't want to introduce yet another one.

...

I'm self-taught in GDScript (and a couple other high level scripting languages), so my programming fundamentals are pretty lacking and I don't know how hard any of this would be to implement under the hood, but I was wondering if the implementation of annotations in GDScript 2.0 makes the above proposal any more workable/worth revisiting?

@lohmaster
Copy link

If I'm not mistaken it's possible to do this in Godot3 overriding getset methods in inherited classes. It seems that this particular issue is where GDscript2.0 is somewhat lacking when compared to old system.

@me2beats
Copy link

me2beats commented May 29, 2023

Does this @override means the property still have same type?

  • If no, I think this is a bad idea to implement, because it will add unnecessary entropy to the code where it will be used. The parent type should remain imo.
  • if yes, I don't think @override is needed. I suggest simply not to use var keyword to indicate that the member value is overridden:
class A:
    var x:int= 1

class B:
    extends A
    x = 2 # ok 
    
class C:
    extends A
    var x = 2 # should still give the error "the variable exists in parent class"
   

@dalexeev
Copy link
Member

If no, I think this is a bad idea to implement

Of course not, this contradicts the Liskov substitution principle. We are talking about overriding the default value, which is already used in the engine.

suggest simply not to use var keyword to indicate that the member value is overridden

It's confusing. Users may think that it is allowed to write code outside of a function, but this is not true.

@dannymate
Copy link

dannymate commented Oct 3, 2023

What about allowing child classes to change variable types if the type specified is a child. For example:

class MyResource
	extends Resource

class A:
    var x: Resource

class B:
    extends A
    @override var x: MyResource 
    
class C:
    extends B
    @override var x: Resource # Give error because it isn't a child of MyResource

Edit: I suppose it's similar to a generic parameter in C#. Without the generics.
Edit2: You can override functions without a special attribute. Why would it be different for variables wouldn't that make it more confusing. Why not add an icon in the editor next to overridden variables and functions?

@dalexeev
Copy link
Member

dalexeev commented Oct 4, 2023

What about allowing child classes to change variable types if the type specified is a child.

This contradicts Liskov substitution principle. Covariance and contravariance do not work for properties:

class A:
    var x: Node
    var y: Node

class B extends A:
    @override var x: Node2D
    @override var y: Object

func _ready() -> void:
    var b: B = B.new()
    var a: A = b

    a.x = Node3D.new() # Valid.
    print(b.x) # Invalid.

    b.y = Resource.new() # Valid.
    print(a.y) # Invalid.

@dannymate
Copy link

Covariance and contravariance do not work for properties

Technically your 'y' wouldn't be allowed as Object is a parent of Node not a child. But yes I do see your point. I suppose that's why generics exist in the first place.

I just wanted a way to get more specific type hints/limit inspector selection in child classes.

@moritztim
Copy link

Related: godotengine/godot#19113

@BasilYes
Copy link

BasilYes commented Feb 15, 2024

Some use case for this proposal. Now I'm working on addon where I use different resources to specify node behavior. So for some extended resources I need to redefine base resource values defaults for user now I'm using some sort of workaround (tool script):

func _init() -> void:
	if not Engine.is_editor_hint() or _lock:
		return
	_lock = preload("res://addons/fast_ui/triggers/ui_trigger_lock.tres")

But it's work not ideal because it's not actually change defaults but override on start (restore to default button shown):
image

@tektrip-biggles
Copy link

Is this suggestion still actively being considered? Apologies for the duplicate feature request submission, for some reason neither this one nor #10020 showed up when I did a cursory search earlier.

I'm currently working on one Godot project and another UE4-based one & was reminded how nice (and extremely quick) this workflow is in UE4:

  1. Implement a blueprint class with behaviour determined by some default values
  2. Create subclasses of that BP with slightly different default values & maybe a function override
  3. Create subclasses of those BPs with similar tweaks & so on...

Conversely in Godot this is the sort of "seemingly trivial" thing that often trips me up or otherwise slows me down at unexpected times & leads to that (imho) "death by a thousand papercuts" feeling when using the engine for anything non-trivial...

@PhillypHenning
Copy link

PhillypHenning commented Jun 30, 2024

I'm new to GDScript so take my suggestion with a grain of salt...
However, if you're trying to market GDScript as an OOP programming language then it makes no sense to not provide a means of abstraction.. abstraction is a fundamental concept in Object-Oriented Programming (OOP) and by your own documentation: "Still, many best practices using Godot involve applying object-oriented programming principles to the scripts and scenes that compose your game."

Consider the following:
abstract_example.gd

class_name AbstractClassExample
extends Node

@export var example_boolean: bool = false


# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass

child_example.gd

extends AbstractClassExample

#var example_boolean = true
#@export var example_boolean = true
#@override var example_boolean = true

# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass

It's clear what I'd like to do. I understand that this can be accomplished in within the _ready function, however, that's not good enough as:

  1. It doesn't update the UI, which makes things confusing for the downstream user
  2. It's round-about and isn't clean or zen (principal of Python which GDScript is heavily inspired by)

It seems to me that this is an oversight, and experienced developers will not enjoy this as it layers the truth behind function calls when, simply put, the solution could be more elegant, clean and user friendly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.