- Name: Utkarsh
- IRC Nickname: simpu
- Email: id.simpu@gmail.com and utkarsh.email@yahoo.com
- Location: India
- Timezone: India (UTC+5:30)
- GitHub: https://github.com/simpuid
- Telegram: @simpu
- Discord: Simpu#8892
-
College: Indian Institute of Technology, Roorkee, India
-
Major: Computer Science and Engineering
-
Current Year: 2nd Year
-
Degree: Bachelor of Technology
Hello there!
I am Utkarsh, a sophomore with major in computer science at the Indian Institute of Technology Roorkee, India. I have been interested in game development since my middle school days way before Godot was open-sourced. I was introduced to game development through Game Maker. Eventually, I gained some programming experience and tried other high-level engines and low-level frameworks. I developed many prototypes (mostly 2d due to a lack of 3d modelling skills). I have some experience in digital arts, thanks to all those sprites I drew for my earlier games. So, I have a keen interest in games, game engines, programming etc.
- In-depth knowledge of C++, interested in the modern part of it.
- Intermediate knowledge of Rust and it's Ownership model. It helps while programming in other languages too.
- Fluent in C#, Java, Kotlin, Go and a little bit in Haxe, Python, and Javascript
- Hands-On experience in SDL2 (Go and C/C++), SFML (C++), Unity (C#), LibGDX (Java and Kotlin), MonoGame(C#) and other similar frameworks/engines.
- Aware of multiple design patterns used in programming and learning more about it.
- Some knowledge of OpenGL, Meson, CMake
Some of my past projects are either prototypes or in collaboration with other students/friends. Here are some other personal projects complete enough for worth mentioning:
- It is a turn-based, multiplayer (different systems) strategy game.
- Works between 2 players within the same sub-net (Unless ports are forwarded).
- It follows p2p architecture, each player has authority over its own units.
- The message system was developed by hand for limited data types, I was unaware of C#'s serialization libraries, but my implementation works anyway.
- That message system is used to communicate between players about their actions turn by turn.
- The networking part is developed over an abstracted TCP socket library called Telepathy.
- Works on mobile and desktop even cross-play is possible.
- The whole game is developed in Unity.
- Developed in a limited 1-month time frame.
- An emulator for intermediate programming language CHIP-8
- Implemented in Go.
- Uses SDL2 for window management and renders in software mode. The renderer of the emulator is developed over the SDL2's surface blit feature.
- Supports configuration for key mapping, window parameter, emulation speed, etc.
- Most of the standard ROMs works without error.
-
An interpreted, esoteric, programming language involving stacks and only stacks. Useful in code golfing.
-
The basic element of this language is stack. You can move elements between stacks or clone elements from one stack to another, that's all.
-
Special stacks are provided to perform specialised tasks.
-
The language is Turing complete, thanks to Brainfuck2Stacky converter in the repository.
-
Implemented in C++ using only the Standard Library (No external dependency)
-
There are many perks to using stack as a basic element of language. Check readme to know more about those perks.
-
This is more like a proof-of-concept project and it works perfectly, another implementation is planned with programmable special stacks, variants as element and some debugging features thanks to Godot's usage of Variant for inspiration.
My experience with Godot is rather new. I like the node system very much. It is similar to Froggy's Component Graph System. I developed some prototypes with the help of docs. Custom language for a game engine seems like a bad idea at first, but I can see the benefit in Hot Reloading, Expression etc. I also like the idea of using the engine for its editor. It helps while developing UI for plugins. The use of Scene Editor to develop UI for a custom plugin is an amazing idea. I haven't touched the 3D part yet. I hope to gain enough experience and speed before October's Ludum Dare.
- Changed
set_meta
toremove_meta
in theCLEAR_GUIDES
menu option #34114. - Remove update condition from
LineEdit::update_placeholder_width
#37123. - Fixes transform gizmo position when
Node
has default transform #37161. - Fixes unnecessary change in animation length property while using the slider #37083.
- Sync
mouse_filter
property inSpinBox
with internalLineEdit
#36660. - Fixed
Node
renaming bug #35319. Curve2D
andCurve3D
revert bug #36644.
- Implement undo-redo feature for
Parameter Paste
in theInspector
#36667. - Improves
PopupMenu
search functionality #36734, the idea is taken from godotengine/godot-proposals#539. - Adds built-in function
angle_diff
togdscript
,mono
andvisual script
#37078, the idea is taken from godotengine/godot-proposals#330. - Added
Fix Invalid Cell
andClear All
action toGridMap
#37136.
Accept it or not, debugging games is very hard. Games involve multiple factors like player input, randomness, etc. Each session of debugging is different due to these factors. Breakpoints are good for finding bugs, but they freeze the whole game. Bugs/Reasons for bad performance can't be found easily using breakpoints. Visual tools are used to find them. One of the visual tools is Godot's Performance Monitor.
Godot's implementation of Performance Monitor monitors the core features like frame rate, physics time, draw calls, memory usage, etc. It visualizes them with the help of line graphs. Users have to roll their monitoring systems to monitor custom features. Godot's Performance Monitor can be used to monitor user-defined features. This proposal provides a solution to add custom performance monitor support in Godot. Adding a custom monitoring system will
- Save the implementation time of a separate user-made monitoring system for each project.
- Most of the user-made monitoring system renders graphs/values on top of the game. So a custom monitoring system in the editor will save screen space. Testing becomes easier.
- Profiling games on remote devices like android phones, iPhone, iPad, etc becomes easier.
Custom monitors are not limited to only profiling. They can be used to monitor other values like bullet count, score, accelerometer's velocity, etc.
Custom Performance Monitors can profile the int
/float
value during a debug session. They cover most of the use cases encountered in small/medium games. Large games need more flexibility. They need to profile different types of data. Custom Profilers can solve this problem. It can help to collect, process and visualize non-generic profiling data like:
- State of different sensors in remote device.
- Taps received by input system.
- Turn history in a turn based game.
- Procedural generators' state.
This proposal provides a solution to use existing debugger code to implement Custom Profiler API.
Editor refers to engine instance running Godot Editor
Game refers to engine instance running game project
- Game adds custom monitors using functions defined in
Performance
class. func add_monitor(id: String, callable: Callable, args: Array)
adds a monitor with suppliedString
asid
,Callable
callable
is called withArray
args
as arguments to get monitor values in future ticks.func remove_monitor(id: String)
removes the monitor associated withid
.Callable
is called in the Game and it should return either anint
or afloat
- Game serializes all returned values of added
Callable
s toArray<Varaint>
and sends them as packetperformance:monitor_frame
to Editor. - Game serializes the custom monitor's
id
toArray<Varaint>
and send them as packetperformance:monitor_id
to Editor when custom monitors are added/removed.
- Editor deserializes
Array<Variant>
from packetperformance:monitor_frame
to custom monitor's data. - Editor deserializes
Array<Variant>
from packetperformance:monitor_id
to custom monitor's ids. CheckBox
s are added according to receivedperformance:monitor_id
in "Custom" section of existing Monitor tab.- Editor draws the graph of custom monitors whose
CheckBox
is checked. - If no data is received in a tick, custom monitor assumes 0 as its value .
- Negative values are clamped to 0 in line graphs.
Profiler UI Scene refers to user-made scene which contains the UI of Custom Profiler. Profiler UI Script is attached to root node
- Since Godot supports multiple debug sessions simultaneously, therefore
add_control_to_dock
approach is not applicable for Profiler UI. Instead,EditorDebuggerNode
takes aPackedScene
, let's call it Profiler UI Scene, of profiler UI and instantiates it to the existingScriptEditorDebugger
's trees. It also adds them to future instances ofScriptEditorDebugger
of new debug sessions.
Profiler UI Script refers to user-made script that handles the UI and communication part of Custom Profiler from Editor.
- The user made Profiler UI Script, attached to the root node of Profiler UI Scene, handles the __Editor __ side of custom profiler through
ScriptEditorDebugger
Custom Profiler Node refers to user-made node that handles the task of collecting, processing and sending profiler data from Game to Editor.
- A user made Custom Profiler Node, attached to main tree of Game through auto-load or other means, handles the Game side of custom profiler through
EngineDebugger
.
This diagram shows the connections between Custom Profiler Node, Profiler UI Script and Profiler UI Scene during debugging 3 instances of Game with a single Editor.
Above diagram shows the working of Custom Profiler in multiple debug sessions.
- Profiler UI Script uses message calls and captures with
ScriptEditorDebugger
to communicate. - Custom Profiler Node uses message calls, captures and profiler calls with
EngineDebugger
singleton to communicate. - Profiler calls means the internal interface provided by
EngineDebugger::Profiler
(toggle
,add
andtick
) - Captures are used for routing the incoming messages to specified functions, as done by
EngineDebugger::Capture
, depending on the starting sub-string of the message. Example-rotation:set
is captured byrotation
with messageset
,game:end
is captured bygame
with messageend
Custom Profiler API mock-up shows the implementing of a simple profiler that shows device's magnetometer direction.
- profiler.gd is used for Custom Profiler Node
- profiler_editor.gd is used for Profiler UI Script. It is attached to root node of Profiler UI Scene.
- plugin.gd registers Custom Profiler Node and Profiler UI Scene
-
MonitorCall
stores the relevant data to call custom monitor function.class MonitorCall{ Callable callable; Vector<Variant> arguments; };
-
OrderedHashMap<String, MonitorCall>
namedmonitor_map
inPerformance
class maps the ids with respectiveMonitorCall
. -
Following methods provide interface to add/remove monitors. They add/remove
MonitorCall
frommonitor_map
.bool add_monitor(const StringName &p_id, const Callable &p_callable, const Vector<Variant> &p_args); void remove_monitor(const StringName &p_id)
-
Dirty flag
bool ids_dirty
inPerformance
class represents the state ofmonitor_map
-
ids_dirty
is set when monitors are added/removed. -
RemoteDebugger::PerformanceProfiler::tick
method is modified to sendperformance:monitor_id
andperformance:monitor_frame
-
If
ids_dirty
is set, thenRemoteDebugger
sends ids ofmonitor_map
under packetperformance:monitor_id
in nextRemoteDebugger::PerformanceProfiler::tick
. -
RemoteDebugger
callsCallable
s ofmonitor_map
and stores them inArray<Variant>
. It sends the array in packetperformance:monitor_data
inRemoteDebugger::PerformanceProfiler::tick
. -
performance:monitor_id
precedesperformance:monitor_data
in a tick. -
Variants
are ordered inArray<Variant>
in same order asmonitor_map
-
MonitorData
stores the required data of custom monitor.class MonitorData{ List<float> history; float max; TreeItem* item; };
history
contains the data history of custom monitor.max
caches the maximum value ofhistory
's items.item
points to theCheckBox
of the custom monitor. -
OrderedHashMap<String,MonitorData>
of namemonitor_data_map
maps ids withMonitorData
-
HashMap<String,int>
namedmonitor_index_map
stores the index of custom monitor's data in received data. -
monitor_data_map
andmonitor_index_map
are member variable ofScriptDebuggerEditor
-
ScriptDebuggerEditor::_parse_message
method is modified to process packets namedperformance:monitor_frame
andperformance:monitor_id
-
ScriptDebuggerEditor
adds new frame to custom monitors while processingperformance:monitor_frame
There are three cases
- Ids present in
monitor_data_map
but not inmonitor_index_map
are of custom monitors which are removed fromGame
but still checked inEditor
- Ids present in
monitor_data_map
and also inmonitor_index_map
are of custom monitors which are active inGame
- Ids not present in
monitor_data_map
but present inmonitor_index_map
represents the case whenperformance:monitor_frame
is processed beforeperformance:monitor_id
. This case will never happen because Game and Editor communicate usingRemoteDebuggerPeerTCP
which ensures ordering of messages.
ScriptDebuggerEditor
follows following procedure for possible 2 cases- 0 is added to
history
ofMonitorData
which was mapped with ids in first case. - Frame data is added to
history
ofMonitorData
which was mapped with ids in second case.index
of ids inmonitor_index_map
is used to get frame data.
- Ids present in
-
ScriptDebuggerEditor
updatesmonitor_data_map
,monitor_index_map
, andTree* perf_monitors
while processingperformance:monitor_id
. The following procedure is followed:ScriptDebuggerEditor
rebuildsmonitor_index_map
using the received data.- A new
OrderedHashMap<String,MonitorData>
namedmonitor_data_map_new
is created. - Contents are moved from
monitor_data_map
tomonitor_data_map_new
. - Ids which are absent in
monitor_index_map
and theirCheckBox
is not checked are dropped in this movement process. monitor_data_map_new
is swapped withmonitor_data_map
.- Unused ids are removed up to this point.
- New custom monitor's ids are added by iterating through received data and checking
monitor_map_data
- The name of
CheckBox
is same as id of corresponding custom monitor. - All
CheckBox
items underCustom
TreeNode
are removed and rebuilt according to newmonitor_data_map
- Previously checked items are checked again after rebuild.
-
ScriptEditorDebugger::_performance_draw()
method is modified to draw the line graphs of custom monitors. -
Same drawing procedure is used to draw the line graph with some modifications.
-
ScriptEditorDebugger::start
method is modified to clear the history of availableMonitorData
inmonitor_map_data
Three classes are exposed to ClassDB
in this implementation.
EngineDebugger
EditorDebuggerNode
Partially exposed already.ScriptEditorDebugger
Partially exposed already.
EngineDebugger
and EditorDebuggerNode
are singletons. Their get_singleton
methods are registered too.
Functions exposed and their brief implementation overview:
It handles register/unregister method of Profiler UI Scene and supplies reference of ScriptEditorDebugger
to Profiler UI Script. It is a singleton available in the Editor.
-
func EditorDebuggerNode.register_profiler_scene(id:String, scene:PackedScene)
It registers
scene
as Profiler UI Scene with givenid
. Current instances ofScriptEditorDebugger
instantiate thisPackedScene
into their tabs. AHashMap<String,Ref<PackedScene>>
, let's call itscene_map
, is used to store the mapping betweenid
andscene
.scene_map
is used byEditorDebuggerNode
, while adding more debuggers (ScriptEditorDebugger
), to instantiate Profiler UI Scene.Needs to be implemented
-
func EditorDebuggerNode.unregister_profiler_scene(id:String)
It removes the entry with given
id
from thescene_map
. Current instances ofScriptEditorDebugger
remove the respective instances of Profiler UI Scene from their tabs too.Needs to be implemented
-
func EditorDebuggerNode.has_profiler_scene(id:String)->bool
Returns whether
id
is inscene_map
Needs to be implemented
-
func EditorDebuggerNode.get_debugger_id(root_node:Node)->int
After Profiler UI Scene is instantiated and before it is added to
ScriptEditorDebugger
's tab tree, an entry is made into aHashMap<ObjectID,int>
, let's call itscene_to_debugger_map
. TheObjectID
of Profiler UI Scene's root node is the key of entry. The id of attachedScriptEditorDebugger
is the value of entry. This function usesscene_to_debugger_map
to return the id of debugger which contains the scene with root noderoot_node
in it's tabs. If none is found, -1 is returned. Removal of Profiler UI Scene updates thescene_to_debugger_map
too.Needs to be implemented
-
func EditorDebuggerNode.get_debugger(id:int)->ScriptEditorDebugger
This function returns the
ScriptEditorDebugger
of respectiveid
. It's already implemented asEditorDebuggerNode::get_debugger
Needs to be exposed
Most functions of Custom Profiler API is already implemented as static functions of EngineDebugger
.
EngineDebugger::Profiler
and EngineDebugger::Capture
are also implemented. I will use them in the API.
-
func EngineDebugger.register_profiler(name:String, toggle:Callable, add:Callable, tick:Callable)
It wraps
EngineDebugger::register_profiler
. ThreeCallable
are used instead of aEngineDebugger::Profiler
. Function signature oftoggle
,add
,tick
arefunc toggle(state:bool, args:Array)
,func add(data:Array)
andfunc tick(frame_time:float, idle_time:float, physics_time:float, physics_frame_time:float)
respectively.Needs to be implemented
-
func EngineDebugger.unregister_profiler(name:String)
It exposes already implemented
EngineDebugger::unregister_profiler
Needs to be exposed
-
func EngineDebugger.is_profiling(name:String)->bool
It exposes already implemented
EngineDebugger::is_profiling
Needs to be exposed
-
func EngineDebugger.has_profiler(name:String)->bool
It exposes already implemented
EngineDebugger::has_profiler
Needs to be exposed
-
func EngineDebugger.register_message_capture(name:String, capture:Callable)
It wraps
EngineDebugger::register_message_capture
.Callable
is used instead ofEngineDebugger::Capture
. Function signature ofcapture
isfunc capture(message:String,args:Array)
Needs to be implemented
-
func EngineDebugger.unregister_message_capture(name:String)
It exposes already implemented
EngineDebugger::unregister_message_capture
Needs to be exposed
-
func EngineDebugger.has_capture(name:String)->bool
It exposes already implemented
EngineDebugger::has_capture
Needs to be exposed
-
func EngineDebugger.profiler_enable(name:String, bool:enable, args:Array)
It exposes already implemented
EngineDebugger::profiler_enable
Needs to be exposed
-
func EngineDebugger.send_message(message:String, data:Array)
It exposes
EngineDebugger
's virtual functionsend_message
Needs to be exposed
-
func EngineDebugger.profiler_add_frame_data(name:String, data:Array)
It exposes already implemented
EngineDebugger::profiler_add_frame_data
Needs to be exposed
-
Unlike
EngineDebugger
there is no support of message captures inScriptEditorDebugger
. Every message is parsed using a long chain of if/else inScriptEditorDebugger::_parse_message
. A capture system similar toEngineDebugger
will be implemented. The system provides a base to decompose the monolithicScriptEditorDebugger::_parse_message
function. -
Using the capture system,
EditorProfiler
,EditorVisualProfiler
,EditorNetworkProfiler
etc can move the parsing code to their respective classes. -
Just like the
EngineDebugger
, the capture system will provide following functions:void ScriptEditorDebugger::register_message_capture(const StringName &p_name, Capture p_func)
void ScriptEditorDebugger::unregister_message_capture(const StringName &p_name)
bool ScriptEditorDebugger::has_capture(const StringName &p_name)
-
Above functions will be exposed using:
-
func ScriptEditorDebugger.register_message_capture(name:String, capture:Callable)
. The function signature ofcapture
isfunc capture(message:String,args:Array)
Needs to be implemented
-
func ScriptEditorDebugger.unregister_message_capture(name:String)
Needs to be implemented
-
func ScriptEditorDebugger.has_capture(name:String)->bool
Needs to be implemented
-
-
ScriptEditorDebugger::_put_msg
is used to send messages. An additional functionvoid ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args)
is implemented with aMutex
just likeRemoteDebugger::send_message
.ScriptEditorDebugger::_put_msg
is called in it with mutex lock. -
func ScriptEditorDebugger.send_message(message:String, data:Array)
exposes thisScriptEditorDebugger::send_message
Needs to be implemented
The project is divided in 5 tasks.
- Implementing the Game side of custom performance monitors.
- Implementing the Editor side of custom performance monitors.
- Implementing the functions for
EditorDebuggerNode
- Implementing the functions for
EngineDebugger
- Implementing the functions for
ScriptEditorDebugger
Task #1 and #2 are part of custom performance monitors.
Task #3, #4 and #5 are part of custom profilers. These tasks are extra. They need more discussion in community bonding period.
During community bonding period, I will explore Godot Debugger's code base, discuss about standard ways of doing things and discuss more about the custom profiler part.
Period | Weeks | Activity |
---|---|---|
May 4 to 1 June | NA | Community Bonding Period |
1 June to 7 June | #1 | Task #1 |
8 June to 28 June | #2, #3 & #4 | Task #2 |
29 June to 5 July | #5 | Phase 1 Evaluation. Start Task #3 |
6 July to 19 July | #6 & #7 | Task #3_ |
20 July to 26 July | #8 | Task #4 |
27 July to 2 August | #9 | Phase 2 Evaluation. Start Task #5 |
3 August to 23 August | #10, #11 & #12 | Task #5 |
24 August to 30 August | #13 | Final Evaluation |
Due to the virus outbreak, there are some chances that my college might shift Summer Vacation for a week or two. So I might not be able to devote 40 hours per week at the start of the program. Besides that, I have no problem with availability.