I know this was aksed and answered a a couple of times e.g.
Solution-wide #define, Is There anyway to #define Constant on a Solution Basis? and How to define a constant globally in C# (like DEBUG).
But in my case I can not use any of the suggested methods:
I'm writing on different "modules" (or plugins if you want so) for UnityProjects (kind of a package providing a certain functionality). The idea is that a developer can load a certain "module" to use in his project by importing a UnityPackage with all scripts and resources in it.
But some of these modules themselves depend on other modules. So what I tried so far was having a class Constants in each module with seperated namespaces and preprocessor definitions.
Module A
#if !MODULE_A
#define MODULE_A // BUT I WOULD NEED THIS GLOBAL NOT ONLY HERE
#endif
namespace Module_A
{
public static class Constants
{
// some constants for this namespace here
}
}
Module B
#if !MODULE_B
#define MODULE_B // BUT I WOULD NEED THIS GLOBAL NOT ONLY HERE
#endif
#if !MODULE_A // WILL BE NOT DEFINED OFCOURSE SINCE #define IS NOT GLOBAL
#error Module A missing!
#else
namespace Module_B
{
public static class Constants
{
// some constants for this namespace here
}
// and other code that might require Module A
}
#endif
But ofcourse this cannot work like this since #defines are not global but only in the current file.
Problem
For this whole idea of modules and a simple "load your modules" I can not ask the user to first make changes to the project or solution settings how e.g. suggested by this answer but instead have to use only the (c#) resources that come imported with the UnityPackage (at least with my current know-how).
Is there any way to somehow set/define those constants for the entire Unity-Project by only importing the module's UnityPackage?
Edit:
I could find a solution for 1 definition in Unity using Assets/msc.rsp. But this still wouldn't work for multiple modules since they would have to write into the same file.
After a lot of searches I've finally been able to put together a surprisingly simple solution I'ld like to share with you:
InitializeOnLoad
Unity has an attribute [InitializeOnLoad]. It tells Unity to initialize according class as soon as
Unity is launched
After any re-compiling of scripts => also after importing a new unitypackage with scripts
static Constructor
In their Running Editor Code On Launch example, they show, how to combine this with a static constructor.
From static-constructors:
A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.
While usually you still would have to create an instance of the class, the static constructor is "instanciated/executed" instantly when the class is initliazed, which we force using the [InitializeOnLoad] attribute.
Scripting Define Symbols
Further Unity actually has project wide defines in the PlayerSettings.
And the good part is: We also have access to them via scripting API:
PlayerSettings.GetScriptingDefineSymbolsForGroup
PlayerSettings.SetScriptingDefineSymbolsForGroup.
So what I did now is the following
Module A
This module has no dependencies but just defines a "global define" in the PlayerSettings. I placed this script somewhere e.g. in Assets/ModuleA/Editor (important is the last folder's name).
using System.Linq;
using UnityEditor;
namespace ModuleA
{
// Will be initialized on load or recompiling
[InitializeOnLoad]
public static class Startup
{
// static constructor is called as soon as class is initialized
static Startup()
{
#region Add Compiler Define
// Get the current defines
// returns a string like "DEFINE_1;DEFINE_2;DEFINE_3"
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
// split into list just to check if my define is already there
var define = defines.Split(';').ToList();
if (!define.Contains("MODULE_A")
{
// if not there already add my define
defines += ";MODULE_A";
}
// and write back the new defines
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);
#endregion
}
}
}
Module B
This module depends on Module A. So itself defines a "global define" (so later Modules can check their dependecies on Module B) but additionally it checks first, if Module A is imported. If Module A is missing, it prints an error to the Debug Console.
(You could as well throw a compiler error using #error SOME TEXT, but for some reason this is not capable of printing out the URL correctly so I decided for the Debug.LogError)
I placed this script somewhere e.g. in Assets/ModuleB/Editor
#if MODULE_A
using System.Linq;
#endif
using UnityEditor;
#if !MODULE_A
using UnityEngine;
#endif
namespace ModuleB
{
// Will be initialized on load or recompiling
[InitializeOnLoad]
public static class Startup
{
// static constructor is called as soon as class is initialized
static Startup()
{
#if !MODULE_A
Debug.LogErrorFormat("! Missing Module Dependency !" +
"\nThe module {0} depends on the module {1}." +
"\n\nDownload it from {2} \n",
"MODULE_B",
"MODULE_A",
"https://Some.page.where./to.find.it/MyModules/ModuleA.unitypackage"
);
#else
// Add Compiler Define
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
var define = defines.Split(';').ToList();
if (!define.Contains("MODULE_B"))
{
defines += ";MODULE_B";
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);
#endif
}
}
}
So later in other scripts of Module B I have two options (both do basically the same)
I can either check everywhere #if MODULE_A to check exactly the module this script relies on
or I can instead check #if MODULE_B to rather check with one line if all dependecies are fulfilled since otherwise I don't define MODULE_B.
On this way I can completely check all dependencies between certain modules which is awesome. The only two flaws I saw until now are:
We have to know how the define (e.g. MODULE_A) looks like for every module and if it is changed in the future it has to be changed in all depending modules as well
The "global define" isn't getting removed in case the module is deleted from the project
But well - which solution is perfect?
In general, the way I would solve this problem in C# is by defining a common set of interfaces that all your modules would contain. I think you can do this with Unity by placing the files from each module in the same location, thus allowing later installations to overwrite those same files (with, obviously, the same content). You would then put editor controls that expose properties to hold instances of those interfaces and then wire them up in the UI. You would test those properties for a value of null to determine which ones are missing.
Common.cs:
public interface IModuleA {}
public interface IModuleB {}
ModuleA.cs
public class ModuleA : IModuleA {}
ModuleB.cs
public class ModuleB : IModuleB
{
public IModuleA ModuleAInstance {get; set;}
private bool IsModuleAPresent()
{
return !ModuleAInstance == null;
}
}
The ideal way to solve it would be with a package manager and proper dependency injection, but doing that with Unity is not straightforward.
Related
I am developing a package for Flutter Apps
There are methods and classes that are useful only for the package itself, and not for the programmer who will import my package, is possible to hide this methods and classes for further implementation?
Example:
DataService.dart
export class DataService{
//Must be visible only for my library
static notifyDataChanged(InternalEvent internalEvent){ ... }
//Must be visible for anyone
static addCallbackOnDataChange(onDataChangeCallback) { ... }
}
InternalEvent.dart
//Must be visible only for my library as well
export class InternalEvent {
...
}
The usual approach to having package-only declarations is to put them in a library in the lib/src/ directory, and not export that library. The other libraries in the package can import the package-only library, but users outside the package are discouraged from importing libraries in lib/src/ directly. (It's not impossible, just something that's discouraged because the package is free to change those libraries without warning).
If the package-only features require access to library private parts of public classes, then they need to be in the same library. The traditional way is then to declare both in a library in lib/src/ and export only the parts of that library which needs to be public:
library myPackage;
export "src/allDeclarations.dart" hide Private, Declarations;
// or, preferably,
export "src/allDeclarations.dart" show Public, Things;
Generally you should only put exported and non-exported declarations in the same library if absolutely necessary. Otherwise the hide/show lists become too cumbersome and it's to easy to forget a declaration in a hide list.
You have a few possibilities:
Making a method/variable private, by prefixing it with _:
class _InternalEvent {}
Use the hide/show directives:
// lib/src/event.dart
class InternalEvent {}
class VisibleEvent {}
// lib/my_package.dart
export 'src/event.dart' hide InternalEvent;
OR
export 'src/event.dart' show VisibleEvent;
For package-private members exists an annotation, #internal.
Using #internal the analyzer emit a warning when:
you export the annotated element (from a file under lib)
a consumer of your package imports the annotated element
Anyway, Dart seems to me to make things really complicated. The need to have members who are neither completely public nor inaccessible from outside the file is elementary, yet no solution provides certainties.
Note that:
the doc says to keep the package-private elements in files under lib/src, yet the consumers of your package will still be able to import them, without even the analyzer producing a warning; it's just a convection;
using the #internal annotation, the analyzer (ie the ide, which rely on the analyzer) produces a warning, but nothing prevents you from compiling the code anyway. The situation improves a little if you increase the severity level of the warning produced by the analyzer when the annotation is not respected. To do this, you need to create an analysis_options.dart file like the following:
analyzer:
errors:
invalid_use_of_internal_member: error #possible values: ignore, info, warning, error
Note that the #internal annotation, like other similar ones (#visibleForTesting, #protected) is part of the meta package, which is included in the Flutter Sdk, but which must be included as a dependency in pure-dart packages.
its so simple
suppose i have a below code in src folder of your lib,
class myClass1 {}
class myClass2 {}
class myClass3 {}
below export statement will make all 3 classes visible/accesible
export 'src/mylib_base.dart' ;
below export statement will make myClass3 visible/accessible and remaining not accessible
export 'src/mylib_base.dart' show myClass3 ;
below export statement will make myClass3 not visible/accessible and remaining accessible
export 'src/mylib_base.dart' hide myClass3 ;
So simply
with hide classes/function ,hide those that you mention and remaining will be shown to end user
with show classes/function ,show those that you mention and remaining will be hide to end user
I have some swift extensions I want to across projects.
I'd like to avoid category pollution though, unless those extensions are requested.
Is it possible to write them so that they only apply if I've done a certain import, like:
import MySwiftExtensions
// Use custom extensions
let x = [1,3,5,7].average()
let y = [1,3,5,7].firstWhere { $0 > 3 }
let z = "campervan".make1337()
I could write these as static methods wrapped in a single letter class (eg: ø.average([1,3,5,7]), like lodash) to achieve the same thing but sometimes you get much more concise usage from instance methods.
You wrote:
I have some swift extensions I want to across projects...
When I have code that I want to use across projects I create a separate framework to hold that code. Then, when I want to use that code in a new project, I embed the framework in that project. Or, for development purposes, I create a workspace that includes the project and the framework. That allows me to work on both at the same time, and then only embed the framework in the final product when it is time to export it.
Once the framework is either embedded or in the same workspace, then you should be able to import it into any individual file in your project with:
import MySwiftExtensions
Any file that does not have the import statement will not have access to the extensions.
EDIT:
Here is a link to a blog that describes how to create a Cocoa Touch Framework. And here is another link that describes in detail how to use workspaces to use frameworks in development projects.
I would like to focus attention on what you reported: "..only apply if I've done a certain import.."
It would also mean you want these extensions can be applyed only to a specific class
As reported in this interesting Apple blog chapter and in the official Apple doc you can handle the "Access Control" of your extension
You can extend a class, structure, or enumeration in any access
context in which the class, structure, or enumeration is available.
Any type members added in an extension have the same default access
level as type members declared in the original type being extended. If
you extend a public or internal type, any new type members you add
will have a default access level of internal. If you extend a private
type, any new type members you add will have a default access level of
private.
Alternatively, you can mark an extension with an explicit access level
modifier (for example, private extension) to set a new default access
level for all members defined within the extension. This new default
can still be overridden within the extension for individual type
members.
/* no access level modifier: default access level will be 'internal' */
extension UIViewSubClass
{
// default access level used: internal
var helloWorld : String {
get {
return "helloWorld"
}
}
}
// modify default access level to public
public extension UIViewSubClass
{
// default access level used: public
var helloWorld : String {
get {
return "helloWorld"
}
}
}
The members of extensions marked private are available within the file where they’re defined, and are not available outside that file. Outside the file where the private extension members were defined, any attempt to use them results in an error, and auto-complete wouldn’t even list them
// modify default access level to private
private extension UIViewSubClass
{
var helloWorld : String {
get {
return "helloWorld"
}
}
}
I don't believe you can do what you want per se, but I've used the following approach to provide functionality to only the specific class that implements an interface:
protocol ExampleProtocol {
}
extension ExampleProtocol where Self: UIViewController{
// extend what you need to here
}
I am trying to define a static var in UnityScript.
The problem is that my script does not reside within a class.
How can i create a global variable that can be accessed from a C# class and
a UnityScript script?
The first thing to do is place the scripts in the correct folders in your Project tab. The script you want to have access must be inside the Standard Assets or the Plugins folder. The other script has to be placed outside these folders. After this step, just call the GetComponent() method as any other Component. Here’s a JavaScript example:
//create a variable to access the C# script
private var csScript : CSharp1;
function Awake()
{
//Get the CSharp Script
csScript = this.GetComponent("CSharp1"); //Don't forget to place the 'CSharp1'
file inside the 'Standard Assets' folder
}
//... code here
Now, here’s a C# example:
using UnityEngine;
using System.Collections;
public class CSharp2 : MonoBehaviour
{
//create a variable to access the JavaScript script
private JS1 jsScript;
void Awake()
{
//Get the JavaScript component
jsScript = this.GetComponent<JS1>(); //Don't forget to place the 'JS1' file inside the 'Standard Assets' folder
}
//...
}
And this is how it’s done. There’s no way to have access to C# and JavaScript simultaneously, as one of the scripts must be at Standard Assets or Plugins folder. Scripts placed inside one of these folders are compiled first, meaning these scripts can’t access the ones outside it, as they are not compiled yet (more information here).
There is a Project With Example here
Let's say I have a contract
public interface IGreeting
{
string SayHelloWorld();
}
And a class in another dll
[Export(typeof(IGreeting))]
public class Greeting : IGreeting
{
public string SayHelloWorld()
{
return "GREETING V1";
}
}
I use directorycatalog to get this DLL. I works fine. When I update my source code like this:
[Export(typeof(IGreeting))]
public class Greeting : IGreeting
{
public string SayHelloWorld()
{
return "GREETING V2";
}
}
and put this new DLL "Next" to the old Greeting DLL Mef doesnt import multiple different Greeting classes but picks 1 out of 2 DLL and exports 2 times from the same class.
So final executing directory looks like this:
MyApp.exe
Greeting_V1.dll
Greeting_V2.dll
I want the application to import 2 Greeting classes with ImportMany. It gives me 2 instances from Greeting_V1.dll. If I delete Greeting_V2.dll it gives me only 1 instance of Greeting_V1.dll.
Well, to me, it looks like you are importing many instances of IGreeting, so in that sense, MEF is doing exactly what it is supposed to do. If you want to replace the instance of Greeting from the V1 assembly with what is in V2, remove the V1 assembly, that way MEF can only load what is available.
It not a MEF problem. The problem is in the loading model of .NET. (or better the way you're objects are loaded by .net)
When MEF loads it returns the correct objects. But when looking for class Greeting when V2 is loaded there is already a Greeting class for V1 dll loaded with the correct class Greeting name for V2 is referring to. And the loader the dll actually referenced by V2 is not loaded.
I have an ATL/COM-based DLL, made using VC++ 6.0. It's used for various executables built using VB6.
I want to add some COM interfaces that will not be accessible via VB6, for the internal use of the DLL only. I want them to be COM interfaces rather than just C++ interfaces due to the fact that I'll be using them on objects which I only have COM interface references to (at the time of use).
That is, I don't know (at the time of use) that this pointer points to a CFoo; I just know that it points to an IFoo, where IFoo is a COM interface, published so that VB6 knows about it. From this IFoo pointer, I want to get an IFooInternal pointer, which every class that implements IFoo (not just CFoo) will implement. Moreover, I don't want IFooInternal to be accessible outside of the DLL itself; for example, I don't want IFooInternal to be accessible by VB6.
Any help with this would be greatly appreciated. Suggestions for alternate ways to accomplish the same overall goal would also be. Thanks in advance.
Answering my own question for the benefit of anyone who finds this thread in the future. However, please note that I am just guessing here, based on experimentation... it seems to work, at least in my specific situation, but I don't know.
First, in some appropriate header file (perhaps Hidden.h), put the following, replacing a newly generated UUID with a newly generated UUID:
#ifndef __Hidden_h__
#define __Hidden_h__
extern "C" {
#ifndef __IHidden_FWD_DEFINED__
#define __IHidden_FWD_DEFINED__
typedef interface IHidden IHidden;
#endif // __IHidden_FWD_DEFINED__
#ifndef __IHidden_INTERFACE_DEFINED__
#define __IHidden_INTERFACE_DEFINED__
EXTERN_C const IID IID_IHidden;
MIDL_INTERFACE("a newly generated UUID")
IHidden : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE get_Something (
long __RPC_FAR *pVal ) = 0;
};
#endif // __IHidden_INTERFACE_DEFINED__
}
#endif // __Hidden_h__
For how to define other types of parameters and such for a function, refer to the C++ header that was generated by MIDL from your IDL file.
Next, in the header for any class that you want to implement this interface, add the interface to the class declaration:
class ATL_NO_VTABLE CBlah :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CBlah, &CLSID_CBlah>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CBlah>,
public IBlah,
public IHidden
Also add it to the COM_MAP:
BEGIN_COM_MAP(CBlah)
COM_INTERFACE_ENTRY(IBlah)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
COM_INTERFACE_ENTRY(IHidden)
END_COM_MAP
And from there on out it's just a matter of adding the standard C++ declarations and definitions of the interface's functions to the class:
public:
STDMETHOD(get_Something)(long *pVal);
(...)
STDMETHODIMP CBlah::get_Something(long *pVal)
{
if ( !pVal )
{
(error handling)
}
*pVal = 37;
return S_OK;
}
I hope this helps someone in the future. I also hope that it doesn't hurt me due to it being incorrect. As I said, though, it seems to work.
This doesn't sound like a problem that necessarily needs COM. Remember: If you get an unmarshalled IFoo* from COM, it is just a normal IFoo* – not some magic COM-Thing. So what you can do is simply define a normal C++-Interface IFooInternal and then dynamic_cast your IFoo* to that.