Content in shared project

Hey,

I have a problem with setup Content in Shared Project (I woul’d like to share my content and code for iOS, Android etc. projects). I’m using VS 2015. Unfortunatelly, there’s no MonoGameContentReference build action in Shared Project. I tried to change this manually in projitems file:

None Include="$(MSBuildThisFileDirectory)Content\Content.mgcb">

I changed “None” to “MonoGameContentReference” and it seems to work but now Content.mgcb disappeared from Visual Studio. The question is how to properly setup content for cross platform solution ? Maybe I shouldn’t use shared project ? It seems to be tricky …

1 Like

Make sure you import the .targets file that defines the MonoGameContentReference build action in the .shproj file by adding the following line:

<Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />

But it’s true that it’s a bit of a hassle setting up a shared project manually like this. Once you get it working it’s real nice for development though :slight_smile:

It still doesn’t work for me … Could you please paste full .shproj file ? I imported manually MonoGame.Content.Builder.targets but still there is no Build Action MonoGameContentReference in VS.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Label="Globals">
    <ProjectGuid>5e5ce63b-00d8-492e-9373-0db7fefc4ab8</ProjectGuid>
    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
  </PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
  <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
  <Import Project="MyGameShared.projitems" Label="Shared" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

It doesn’t work in Visual Studio, you have to manually import the project and set the build action in the .shproj file.

Seriously ? Is it such a mess to port game in Monogame ? I heard that Shared Project is best option to cross platform solution but I have to manually change shproj file and even I couldn’t manage content (mgcb) from Visual Studio ? Is it really the way that developers work on cross platform games with Monogame ? No user-friendly solution ?

If you don’t want to do it like this you can use PCL or Protobuild.

OK, thanks, I give try with Shared Project … But I’m stuck at beginning. I have to specify platform in MGCB file:

/outputDir:bin/Android
/intermediateDir:obj/Android
/platform:Android
/config:
/profile:Reach
/compress:False

How to make it cross-platform ? I couldn’t find any sample that uses Shared Project so all I could do is to dig into it. Is there any constant that would choose target platform during compile time ? I want to link Shared Project in Android, iOS etc. I love Monogame but it’s pain to find out how to setup it.

If you use the MonoGameContentReference the target platform is handled for you. You can just omit the /platform line in your .mgcb. So to recap, you just have to import the MonoGame.Content.Builder.targets file in your .shproj and include the .mgcb file with the build action set to MonoGameContentReference.

Thanks a lot ! There’s also target platform NativeCode, I thought it’s defined for cross-platform content but maybe I’m wrong. I have one more question, is it possible to add references (DLL or other project) to Shared Project ? As per Xamarin forum, it seems that it’s not possible, the only way it will work is to add reference to “main” project, and Shared Project could access this during compile time. Am I correct ?

That’s not a MonoGame thing.

Nope, shared projects really just contain code, they’re not real projects in that they can’t be compiled by itself. You need a ‘real’ project that uses the shared project to actually use the code. It’s then compiled together. So in that aspect a shared project is very different from a PCL or regular class library.

Thanks man, you explained it well :slight_smile: I have In App Purchases in my game, it works differently for Windows Phone / Android / iOS. I would like to keep some kind of abstraction in my core, let’s say:

class InAppPurchases
{
    public abstract bool HasProductPurchased(String productId);
    public abstract async void PurchaseProduct(String productId, Callback);

}

In Android, WP, iOS projects I would like to keep implementation of this abstract. Is it possible ? To “inject” specific implementation ? Or should I use preprocessor macros, like

#ifdef ANDROID:
    return AndroidInAppPurchases();
#else:
    return WindowsPhoneInAppPurchases();
#endif

In my opinion, it will be messy to keep it with macros, because probably I have to also keep imports (uses) also wrapped with #IFDEF macros. I appreciate any help. I have a lot of such problems during port of game, like access to storage, in app purchases, highscores etc., so I would like to have good approach. I think the best way is to create such abstraction layer in shared core, but how to inject specific implementation ?

I wouldn’t recommend preprocessor macros except for tiny stuff, because it can get messy fast. An abstraction layer is nice, just define the API in an interface/abstract class in the shared project and have an implementation in your platform-specific projects. A nice way to inject something like that is to use the ServiceContainer in Game. In your game you can register any service using this.Services.AddService. Then you can later look up a service by type. So you can register your InAppPurchases instance under the type of the common base class/interface.

Alternatively you can use partial classes and methods. If you define a class partial you can have additional implementation of the class in a seperate file. So you can have a partial class InAppPurchases in the shared project and in each platform-specific project have a file InAppPurchases.[Platform].cs that expands the class with platform specific functionality. This is what’s used in MonoGame itself in combination with Protobuild for building the projects and solutions. In MG there’s a pattern we use:

// In MyClass.cs
public partial class MyClass
{
    public void DoSomething()
    {
        // implement shared behavior
        // ...

        // Now call into the platform specific method
        PlatformDoSomething();

        // implement shared behavior
        // ...
    }
}

// In MyClass.Windows.cs
public partial class MyClass
{
    public void PlatformDoSomething()
    {
        // windows specific stuff goes here
    }
}

I had no idea about partial classes and I think it will fit perfectly for me but … partial methods cannot return value. For example, I want to have abstract method

bool InAppPurchases.HasProductPurchased(String productId) 

and it will be implemented in iOS, Android, etc. projects. It will be great solution to problem but I need to return value. How you solve this puzzles in MonoGame ?

Partial methods cannot, but you can use the pattern I showed above.

// In MyClass.cs
public partial class MyClass
{
    public void DoSomething()
    {
        // implement shared behavior
        // ...

        // Now call into the platform specific method
        var obj = PlatformDoSomething();

        // implement shared behavior
        // ...
    }
}

// In MyClass.Windows.cs
public partial class MyClass
{
    public object PlatformDoSomething()
    {
        // windows specific stuff goes here
         return something;
    }
}
1 Like

Just awesome, with partial classes it’s quite easy job to port game to other platforms :slight_smile: Really thank you.

1 Like

Hello,

I’m following your instructions for sharing content using a shared project. I has modified the .shproj file adding the line <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
Please, tell me how I could set the build action for content.mcgb. What another file I need to modify ? and how?

Thank you

You add the Content.mgcb file to your shared project by adding an entry for it in your .projitems file.

  <ItemGroup>
    <MonoGameContentReference Include="$(MSBuildThisFileDirectory)Content\Content.mgcb" />
  </ItemGroup>
1 Like

Finally it worked using “ApplicationDefinition” in build action property

<ItemGroup>
    <ApplicationDefinition Include="$(MSBuildThisFileDirectory)Content\Content.mgcb" />
 </ItemGroup>

Thank you very much