diff options
| author | Loek Le Blansch <loek@pipeframe.xyz> | 2024-10-18 14:37:21 +0200 | 
|---|---|---|
| committer | Loek Le Blansch <loek@pipeframe.xyz> | 2024-10-18 14:37:21 +0200 | 
| commit | 69f8fcfb593641174b3a83049ad4acc1abf1a102 (patch) | |
| tree | 270d22cded77e3a1e5773626c891e327219c833f | |
| parent | 4a40378f58160212c0c1c42552a1301e3a498037 (diff) | |
add scripting design documentation
| -rw-r--r-- | design.tex | 84 | ||||
| -rw-r--r-- | img/class-scripts.puml | 48 | ||||
| -rw-r--r-- | reqs.toml | 33 | 
3 files changed, 165 insertions, 0 deletions
| @@ -36,6 +36,90 @@ workflows.  \subsection{Scripting} +The scripting interface was designed around a `target' \gls{api} (described by +\cref{req:script:interface,req:script:user-class,req:script:direct-instance,req:script:direct-run}). +An example of this \gls{api} is shown below:\noparbreak + +\begin{blockcode} +class MyScript : public BehaviorScript { +	void update() { +		// update code here +	} +	// init() also exists, but is empty by default +}; + +{ // in scene initialization +	GameObject & obj = ...; +	obj.add_component<MyScript>(); +} +\end{blockcode} + +The above call to \codeinline{GameObject::add_component} cannot work correctly +without significantly increasing the complexity of the component manager, so the +following restrictions were taken into account when creating the script system +architecture:\noparbreak + +\begin{itemize} +	\item The first template parameter passed to \codeinline{GameObject::add_component} +		\emph{must} be a base `script \emph{component}' class, so each derived user +		script class is instantiated in the same generic script list. +	\item C++ does not allow passing types (i.e.~\codeinline{MyScript} in this case) as +		function parameters, so a function call like +		\codeinline{add_component<BehaviorScript>(MyScript)} cannot be realized. +\end{itemize} + +\subsubsection{Architecture} + +The restrictions detailed at the start of this section are mitigated as +follows:\noparbreak + +\begin{itemize} +	\item User scripts are split into two classes--- +		\begin{enumerate} +			\item a script \emph{interface} class (\codeinline{Script}) +			\item a script \emph{component} class (\codeinline{BehaviorScript}) +		\end{enumerate} +	\item \codeinline{GameObject::add_component} receives the script \emph{component} +		as template parameter +	\item \codeinline{GameObject::add_component} now always returns a reference to the +		component instance +	\item The script component class has a setter function that takes a template +		parameter for classes derived from the base script \emph{interface} class +\end{itemize} + +\Cref{fig:class-scripts} shows the resulting structure as a class diagram. It +contains the following classes:\noparbreak +\begin{description} +	\item[Script] This is the script \emph{interface}, and is used by the game +		programmer to create derived script classes. All methods in this class are +		declared virtual and have an empty implementation. + +		This class' methods are protected by default, and a friend relation to +		\codeinline{ScriptSystem} is used to ensure only \codeinline{ScriptSystem} is +		able to call these methods. + +		Only classes derived from \codeinline{Script} can be used with +		\codeinline{BehaviorScript::set_script}'s template parameter \codeinline{T}. This +		function returns a reference to the \codeinline{BehaviorScript} instance it was +		called on so it can be chained after the call to +		\codeinline{GameObject::add_component}. +	\item[BehaviorScript] +		This is the script \emph{component}, and is given as the template parameter to +		\codeinline{GameObject::add_component}. + +		This class also uses a friend relation to \codeinline{ScriptSystem} to restrict +		access to its private reference member \codeinline{script}. +	\item[ScriptSystem] This is the system class that runs the methods implemented in +		the derivative instances of \codeinline{Script}. +\end{description} + +\begin{figure} +	\centering +	\includepumldiag{img/class-scripts.puml} +	\caption{User script class diagram} +	\label{fig:class-scripts} +\end{figure} +  \subsection{Audio}  \subsubsection{Library} diff --git a/img/class-scripts.puml b/img/class-scripts.puml new file mode 100644 index 0000000..8fc36c9 --- /dev/null +++ b/img/class-scripts.puml @@ -0,0 +1,48 @@ +@startuml +!include theme.ipuml +skinparam Linetype ortho +skinparam Nodesep 75 +skinparam Ranksep 30 + +class ComponentManager <<irrelevant>> + +package api { +	class Component <<irrelevant>> + +	class Script { +		# init() <<virtual>> +		# update() <<virtual>> +		-- +		- Script() +	} + +	class BehaviorScript { +		# BehaviorScript() +		+ ~BehaviorScript() +		-- +		+ set_script<T>() : this & +		-- +		# script : Script * +	} + +	BehaviorScript -u-|> Component +	Script .u.> BehaviorScript +} + +class System <<irrelevant>> +class ScriptSystem <<Singleton>> { +	+ get_instance() : ScriptSystem & <<static>> +	+ update() +	-- +	- ScriptSystem() +	- ~ScriptSystem() +} + +System <|-- ScriptSystem +ScriptSystem .[norank]> ComponentManager + +ScriptSystem .[norank]> api.Script : < friend +ScriptSystem .[norank]> api.BehaviorScript : < friend +ComponentManager .[norank]> api.BehaviorScript : < friend + +@enduml @@ -84,3 +84,36 @@ Windows.  # TODO: library documentation as quality factor?  # TODO: modularity over less libraries? (i.e. why don't we just SDL2 everything?) +[script:interface] +type = 'system' +priority = 'must' +description = ''' +There is a base \codeinline{Script} class that has empty default +implementations for functions that may be implemented by the game programmer. +''' + +[script:user-class] +type = 'system' +priority = 'must' +description = ''' +The game programmer implements scripts by creating classes derived from the +\codeinline{Script} class. +''' + +[script:direct-instance] +type = 'system' +priority = 'must' +description = ''' +Unless explicitly changed by the game programmer, derived script classes cannot +be instantiated directly, and must be instantiated by the component manager. +''' + +[script:direct-run] +type = 'system' +priority = 'must' +description = ''' +Unless explicitly changed by the game programmer, methods on instances of +\codeinline{Script} (and derivative) classes cannot be called directly, and +must be called by the script system. +''' + |