├── .gitignore
├── Documentation
├── DebugTools.png
└── Logo.png
├── LICENSE
├── NuGet
└── Comora.nuspec
├── README.md
└── Sources
├── Comora.Sample.macOS
├── AppDelegate.cs
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── AppIcon-128.png
│ │ ├── AppIcon-128@2x.png
│ │ ├── AppIcon-16.png
│ │ ├── AppIcon-16@2x.png
│ │ ├── AppIcon-256.png
│ │ ├── AppIcon-256@2x.png
│ │ ├── AppIcon-32.png
│ │ ├── AppIcon-32@2x.png
│ │ ├── AppIcon-512.png
│ │ ├── AppIcon-512@2x.png
│ │ └── Contents.json
│ └── Contents.json
├── Comora.Sample.macOS.csproj
├── Entitlements.plist
├── Game1.cs
├── Info.plist
└── packages.config
├── Comora.sln
└── Comora
├── AspectMode.cs
├── Camera.cs
├── Comora.csproj
├── Diagnostics
├── DebugLayer.cs
├── FpsCounter.cs
├── Grid.cs
├── IFpsCounter.cs
├── IGrid.cs
└── PixelFont.cs
├── ICamera.cs
├── Properties
└── AssemblyInfo.cs
├── SpriteBatchExtensions.cs
├── Transform.cs
└── packages.config
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
--------------------------------------------------------------------------------
/Documentation/DebugTools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Documentation/DebugTools.png
--------------------------------------------------------------------------------
/Documentation/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Documentation/Logo.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Aloïs Deniel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NuGet/Comora.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Comora
5 | $version$
6 | Comora
7 | Aloïs Deniel
8 | Aloïs Deniel
9 | https://github.com/aloisdeniel/Comora/blob/master/LICENSE
10 | https://github.com/aloisdeniel/Comora
11 | https://raw.githubusercontent.com/aloisdeniel/Comora/master/Documentation/Logo.png
12 | false
13 | A simple 2D camera for Monogame.
14 | A simple 2D camera for Monogame.
15 | monogame, camera, matrix, transform, viewport
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Comora
4 |
5 | A simple 2D camera for [Monogame](http://www.monogame.net/).
6 |
7 | ## Install
8 |
9 | Available on NuGet
10 |
11 | [](https://www.nuget.org/packages/Comora/)
12 |
13 | ## Quickstart
14 |
15 | ```sharp
16 | private Camera camera;
17 |
18 | protected override void Initialize()
19 | {
20 | this.camera = new Camera(this.graphics);
21 |
22 | base.Initialize();
23 | }
24 |
25 | protected override void Update(GameTime gameTime)
26 | {
27 | this.camera.Update(gameTime);
28 |
29 | this.camera.Position = Mouse.GetState().Position.ToVector2();
30 |
31 | base.Update(gameTime);
32 | }
33 |
34 | protected override void Draw(GameTime gameTime)
35 | {
36 | graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
37 |
38 | spriteBatch.Begin(this.camera);
39 | // Draw here
40 | spriteBatch.End();
41 |
42 | base.Draw(gameTime);
43 | }
44 |
45 | ```
46 |
47 | ## Draw
48 |
49 | Through the `SpriteBatch` extension methods, simply call `Begin` as usual, but with your camera :
50 |
51 | ```csharp
52 | spriteBatch.Begin(this.camera);
53 | ```
54 |
55 | You can add a parralax factor too :
56 |
57 | ```csharp
58 | spriteBatch.Begin(this.camera, new Vector2(0.85f,0.85f));
59 | ```
60 | ## Animations
61 |
62 | You can add animation by applying [Transform](https://www.github.com/aloisdeniel/Transform) velocities or tweens onto the the camera's `Transform` property.
63 |
64 | ## Conversion
65 |
66 | You can convert a point from screen unit to world :
67 |
68 | ```csharp
69 | var screenPosition = Vector.Zero;
70 | var worldPosition = Vector.Zero;
71 | camera.ToWorld(ref screenPosition, out worldPosition);
72 | ```
73 |
74 | Converting world coordinates to screen coordinates is also possible.
75 |
76 | ```csharp
77 | var screenPosition = Vector.Zero;
78 | var worldPosition = Vector.Zero;
79 | camera.ToScreen(ref worldPosition, out screenPosition);
80 | ```
81 |
82 | ## Debug tools
83 |
84 | 
85 |
86 | The framework provide several tools for debugging purpose.
87 |
88 | ```csharp
89 | camera.Debug.IsVisible = Keyboard.GetState().IsKeyDown(Keys.F1);
90 | ```
91 |
92 | Make sure to load content too :
93 |
94 | ```csharp
95 | this.camera.LoadContent(GraphicsDevice);
96 | ```
97 |
98 | And then draw it with `Draw` extension method. You don't have to call `spriteBatch.Begin` and `spriteBatch.End` which are already invoked by the method.
99 |
100 | ```csharp
101 | this.spriteBatch.Draw(this.camera.Debug);
102 | ```
103 |
104 | ### FPS
105 |
106 | The number of frame per seconds will be displayed on the layer.
107 |
108 | ### Grid
109 |
110 | A visual grid for world units.
111 |
112 | ```csharp
113 | // To draw a white line of size 2 every 50 world units
114 | // and a red one of size 4 every 200 world units.
115 | camera.Debug.Grid.AddLines(50, Color.White, 2);
116 | camera.Debug.Grid.AddLines(200, Color.Red, 4);
117 | ```
118 |
119 | ## Roadmap / Ideas
120 |
121 | * Integrate shaders for easy screen effects.
122 | * Add more built-in animations.
123 | * Clean and improve debug layer.
124 |
125 | ## Why?
126 |
127 | "Seriously, just a camera ?" ... Yes! I often see full engines with tons of stuff I never need. I'm looking about modularity and easy APIs (like many awesome [LÖVE](https://love2d.org/) libraries) and that's why I created **Comora**. I'm sure this could be useful for someone else.
128 |
129 | ## Thanks
130 |
131 | * [david-gouveia](http://www.david-gouveia.com/portfolio/2d-camera-with-parallax-scrolling-in-xna/)
132 | * [Monogame.Extended](https://github.com/craftworkgames/MonoGame.Extended)
133 |
134 | ## Contributions
135 |
136 | Contributions are welcome! If you find a bug please report it and if you want a feature please report it.
137 |
138 | If you want to contribute code please file an issue and create a branch off of the current dev branch and file a pull request.
139 |
140 | ### License
141 |
142 | MIT © [Aloïs Deniel](http://aloisdeniel.github.io)
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | namespace Comora.Sample.macOS
2 | {
3 | using AppKit;
4 | using Foundation;
5 |
6 | [Register("AppDelegate")]
7 | class Program : NSApplicationDelegate
8 | {
9 | private static Game1 game;
10 |
11 | internal static void RunGame()
12 | {
13 | game = new Game1();
14 | game.Window.AllowUserResizing = true;
15 | game.Run();
16 | }
17 |
18 | ///
19 | /// The main entry point for the application.
20 | ///
21 | static void Main(string[] args)
22 | {
23 | NSApplication.Init();
24 |
25 | using (var p = new NSAutoreleasePool())
26 | {
27 | NSApplication.SharedApplication.Delegate = new Program();
28 | NSApplication.Main(args);
29 | }
30 | }
31 |
32 | public override void DidFinishLaunching(NSNotification notification)
33 | {
34 | RunGame();
35 | }
36 |
37 | public override bool ApplicationShouldTerminateAfterLastWindowClosed(NSApplication sender)
38 | {
39 | return true;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-ad/Comora/d3a9b444b80f5290f3a87399f1d6d6560f524fe3/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "filename": "AppIcon-16.png",
5 | "size": "16x16",
6 | "scale": "1x",
7 | "idiom": "mac"
8 | },
9 | {
10 | "filename": "AppIcon-16@2x.png",
11 | "size": "16x16",
12 | "scale": "2x",
13 | "idiom": "mac"
14 | },
15 | {
16 | "filename": "AppIcon-32.png",
17 | "size": "32x32",
18 | "scale": "1x",
19 | "idiom": "mac"
20 | },
21 | {
22 | "filename": "AppIcon-32@2x.png",
23 | "size": "32x32",
24 | "scale": "2x",
25 | "idiom": "mac"
26 | },
27 | {
28 | "filename": "AppIcon-128.png",
29 | "size": "128x128",
30 | "scale": "1x",
31 | "idiom": "mac"
32 | },
33 | {
34 | "filename": "AppIcon-128@2x.png",
35 | "size": "128x128",
36 | "scale": "2x",
37 | "idiom": "mac"
38 | },
39 | {
40 | "filename": "AppIcon-256.png",
41 | "size": "256x256",
42 | "scale": "1x",
43 | "idiom": "mac"
44 | },
45 | {
46 | "filename": "AppIcon-256@2x.png",
47 | "size": "256x256",
48 | "scale": "2x",
49 | "idiom": "mac"
50 | },
51 | {
52 | "filename": "AppIcon-512.png",
53 | "size": "512x512",
54 | "scale": "1x",
55 | "idiom": "mac"
56 | },
57 | {
58 | "filename": "AppIcon-512@2x.png",
59 | "size": "512x512",
60 | "scale": "2x",
61 | "idiom": "mac"
62 | }
63 | ],
64 | "info": {
65 | "version": 1,
66 | "author": "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Comora.Sample.macOS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {F2496309-6E07-4FFB-829C-B5B4F833525D}
7 | {A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
8 | Exe
9 | Comora.Sample.macOS
10 | Comora.Sample
11 | v2.0
12 | Xamarin.Mac
13 | Resources
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug
20 | DEBUG;
21 | prompt
22 | 4
23 | false
24 | Mac Developer
25 | false
26 | false
27 | false
28 | true
29 | true
30 | true
31 |
32 |
33 |
34 |
35 |
36 | pdbonly
37 | true
38 | bin\Release
39 |
40 | prompt
41 | 4
42 | false
43 | true
44 | false
45 | true
46 | true
47 | true
48 | SdkOnly
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | ..\packages\MonoGame.Framework.MacOS.3.6.0.1625\lib\XamarinMac\MonoGame.Framework.dll
58 |
59 |
60 | ..\packages\MonoGame.Framework.MacOS.3.6.0.1625\lib\XamarinMac\OpenTK.dll
61 |
62 |
63 | ..\packages\MonoGame.Framework.MacOS.3.6.0.1625\lib\XamarinMac\Tao.Sdl.dll
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | {EF814457-2BEF-4D6E-BE2A-7E660EC8BBF6}
95 | Comora
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Entitlements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Game1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Xna.Framework;
4 | using Microsoft.Xna.Framework.Graphics;
5 | using Microsoft.Xna.Framework.Input;
6 | using System.Diagnostics;
7 |
8 | namespace Comora.Sample
9 | {
10 | ///
11 | /// This is the main type for your game.
12 | ///
13 | public class Game1 : Game
14 | {
15 | GraphicsDeviceManager graphics;
16 | SpriteBatch spriteBatch;
17 | Texture2D pixel;
18 | ICamera camera, parralax;
19 | List> rectangles;
20 | List> rectangles2;
21 |
22 | public Game1()
23 | {
24 | graphics = new GraphicsDeviceManager(this);
25 | this.Window.AllowUserResizing = true;
26 | graphics.PreferredBackBufferWidth = 1000;
27 | graphics.PreferredBackBufferHeight = 500;
28 | Content.RootDirectory = "Content";
29 | }
30 |
31 | ///
32 | /// Allows the game to perform any initialization it needs to before starting to run.
33 | /// This is where it can query for any required services and load any non-graphic
34 | /// related content. Calling base.Initialize will enumerate through any components
35 | /// and initialize them as well.
36 | ///
37 | protected override void Initialize()
38 | {
39 | this.camera = new Camera(this.graphics.GraphicsDevice);
40 | this.camera.Width = 400;
41 | this.camera.Height = 400;
42 | this.camera.ResizeMode = AspectMode.FillUniform;
43 | this.camera.Debug.Grid.AddLines(50, Color.White, 2);
44 | this.camera.Debug.Grid.AddLines(200, Color.Cyan, 4);
45 |
46 | this.parralax = this.camera.Clone();
47 |
48 | var random = new Random();
49 | this.rectangles = new List>();
50 | for (int i = 0; i < 500; i++)
51 | {
52 | var rect = new Rectangle(random.Next(-1000, 1000), random.Next(-1000, 1000), random.Next(50, 100), random.Next(50, 100));
53 | var color = new Color(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255), 120);
54 | this.rectangles.Add(new Tuple(rect, color));
55 | }
56 | this.rectangles2 = new List>();
57 | for (int i = 0; i < 500; i++)
58 | {
59 | var rect = new Rectangle(random.Next(-1000, 1000), random.Next(-1000, 1000), random.Next(50, 100), random.Next(50, 100));
60 | var color = new Color(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255));
61 | this.rectangles2.Add(new Tuple(rect, color));
62 | }
63 |
64 | base.Initialize();
65 | }
66 |
67 | ///
68 | /// LoadContent will be called once per game and is the place to load
69 | /// all of your content.
70 | ///
71 | protected override void LoadContent()
72 | {
73 | // Create a new SpriteBatch, which can be used to draw textures.
74 | spriteBatch = new SpriteBatch(GraphicsDevice);
75 |
76 | pixel = new Texture2D(GraphicsDevice, 1, 1);
77 | pixel.SetData(new Color[] { Color.White });
78 |
79 | this.camera.LoadContent();
80 | }
81 |
82 | KeyboardState previousState;
83 |
84 | ///
85 | /// Allows the game to run logic such as updating the world,
86 | /// checking for collisions, gathering input, and playing audio.
87 | ///
88 | /// Provides a snapshot of timing values.
89 | protected override void Update(GameTime gameTime)
90 | {
91 | // For Mobile devices, this logic will close the Game when the Back button is pressed
92 | // Exit() is obsolete on iOS
93 | #if !__IOS__ && !__TVOS__
94 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
95 | Exit();
96 | #endif
97 |
98 | var state = Keyboard.GetState();
99 |
100 | if (Keyboard.GetState().IsKeyDown(Keys.Space))
101 | {
102 | var position = Mouse.GetState().Position.ToVector2();
103 | position -= new Vector2(this.GraphicsDevice.Viewport.Width, this.GraphicsDevice.Viewport.Height) / 2;
104 |
105 | this.camera.Position = position;
106 | this.parralax.Position = position * 0.85f;
107 | }
108 |
109 | if (Keyboard.GetState().IsKeyDown(Keys.Z))
110 | {
111 | var zoom = Math.Max(0.1, Mouse.GetState().Position.ToVector2().Y / this.camera.Height);
112 | this.camera.Zoom = (float)zoom;
113 | this.parralax.Zoom = (float)zoom;
114 | }
115 |
116 | if (Keyboard.GetState().IsKeyDown(Keys.R))
117 | {
118 | var r = Math.Max(0, Mouse.GetState().Position.ToVector2().Y / this.camera.Height) * Math.PI * 2;
119 | this.camera.Rotation = this.parralax.Rotation = (float)r;
120 | }
121 |
122 | if (!this.previousState.IsKeyDown(Keys.Enter) && state.IsKeyDown(Keys.Enter))
123 | {
124 | this.camera.ResizeMode = (AspectMode)((((int)this.camera.ResizeMode + 1) % 3));
125 | }
126 |
127 | if (!this.previousState.IsKeyDown(Keys.G) && state.IsKeyDown(Keys.G))
128 | {
129 | this.camera.Debug.IsVisible = !this.camera.Debug.IsVisible;
130 | }
131 |
132 | if (!this.previousState.IsKeyDown(Keys.M) && state.IsKeyDown(Keys.M))
133 | {
134 | this.camera.ResizeMode = (AspectMode)((((int)this.camera.ResizeMode) + 1) % 3);
135 | Debug.WriteLine($"MODE:{this.camera.ResizeMode}");
136 | }
137 |
138 | this.previousState = state;
139 |
140 | this.camera.Update(gameTime);
141 |
142 | base.Update(gameTime);
143 | }
144 |
145 | ///
146 | /// This is called when the game should draw itself.
147 | ///
148 | /// Provides a snapshot of timing values.
149 | protected override void Draw(GameTime gameTime)
150 | {
151 | graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
152 |
153 | spriteBatch.Begin(this.parralax);
154 |
155 | foreach (var rect in this.rectangles)
156 | {
157 | spriteBatch.Draw(pixel, rect.Item1, rect.Item2);
158 | }
159 |
160 | var destination = new Rectangle();
161 | var source = new Rectangle();
162 |
163 | spriteBatch.Draw(pixel, destinationRectangle: destination, sourceRectangle: source);
164 |
165 | spriteBatch.End();
166 |
167 | spriteBatch.Begin(this.camera);
168 |
169 | foreach (var rect in this.rectangles2)
170 | {
171 | spriteBatch.Draw(pixel, rect.Item1, rect.Item2);
172 | }
173 |
174 | var size = new Vector2(6,6);
175 | spriteBatch.Draw(pixel, position: this.camera.Transform.AbsolutePosition - new Vector2(3, 3),scale: size, color: Color.Red, rotation: 0);
176 |
177 | spriteBatch.End();
178 |
179 | this.spriteBatch.Draw(this.camera.Debug);
180 |
181 | base.Draw(gameTime);
182 | }
183 | }
184 | }
185 |
186 |
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleName
6 | Comora.Sample
7 | CFBundleIdentifier
8 | com.comora-sample
9 | CFBundleShortVersionString
10 | 1.0
11 | CFBundleVersion
12 | 1
13 | LSMinimumSystemVersion
14 | 10.12
15 | CFBundleDevelopmentRegion
16 | en
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundlePackageType
20 | APPL
21 | CFBundleSignature
22 | ????
23 | NSHumanReadableCopyright
24 | ${AuthorCopyright}
25 | NSPrincipalClass
26 | NSApplication
27 | XSAppIconAssets
28 | Assets.xcassets/AppIcon.appiconset
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Sources/Comora.Sample.macOS/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Sources/Comora.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comora", "Comora\Comora.csproj", "{EF814457-2BEF-4D6E-BE2A-7E660EC8BBF6}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comora.Sample.macOS", "Comora.Sample.macOS\Comora.Sample.macOS.csproj", "{F2496309-6E07-4FFB-829C-B5B4F833525D}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {EF814457-2BEF-4D6E-BE2A-7E660EC8BBF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {EF814457-2BEF-4D6E-BE2A-7E660EC8BBF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {EF814457-2BEF-4D6E-BE2A-7E660EC8BBF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {EF814457-2BEF-4D6E-BE2A-7E660EC8BBF6}.Release|Any CPU.Build.0 = Release|Any CPU
18 | {F2496309-6E07-4FFB-829C-B5B4F833525D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {F2496309-6E07-4FFB-829C-B5B4F833525D}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {F2496309-6E07-4FFB-829C-B5B4F833525D}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/Sources/Comora/AspectMode.cs:
--------------------------------------------------------------------------------
1 | namespace Comora
2 | {
3 | public enum AspectMode
4 | {
5 | Expand,
6 | FillStretch,
7 | FillUniform,
8 | }
9 | }
--------------------------------------------------------------------------------
/Sources/Comora/Camera.cs:
--------------------------------------------------------------------------------
1 | namespace Comora
2 | {
3 | using System;
4 | using Diagnostics;
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Graphics;
7 | using Transform;
8 |
9 | ///
10 | /// Represents the point of view.
11 | ///
12 | public class Camera : ICamera
13 | {
14 | public Camera(GraphicsDevice device)
15 | {
16 | this.device = device;
17 | this.Debug = new DebugLayer(this);
18 | this.Transform = new Transform2D();
19 | this.viewport = new Vector2(this.device.Viewport.Width, this.device.Viewport.Height);
20 | this.viewportOffset = new Transform2D
21 | {
22 | Parent = this.Transform,
23 | };
24 | this.UpdateOffset();
25 | }
26 |
27 | #region Properties
28 |
29 | private GraphicsDevice device;
30 |
31 | private Transform2D viewportOffset;
32 |
33 | private float? width, height;
34 |
35 | private AspectMode aspectMode;
36 |
37 | private Vector2 viewport;
38 |
39 | #endregion
40 |
41 | #region Properties
42 |
43 | public Transform2D ViewportOffset => this.viewportOffset;
44 |
45 | public Transform2D Transform { get; set; }
46 |
47 | public DebugLayer Debug { get; private set; }
48 |
49 | public AspectMode ResizeMode
50 | {
51 | get => this.aspectMode;
52 | set
53 | {
54 | if(this.aspectMode != value)
55 | {
56 | this.aspectMode = value;
57 |
58 | if(value == AspectMode.Expand)
59 | {
60 | this.viewportOffset.Scale = Vector2.One;
61 | }
62 |
63 | this.UpdateOffset();
64 | }
65 | }
66 | }
67 |
68 | public float Width
69 | {
70 | get
71 | {
72 | if(this.ResizeMode == AspectMode.Expand)
73 | return this.viewport.X;
74 |
75 | return this.width ?? this.viewport.X;
76 | }
77 | set => this.width = value;
78 | }
79 |
80 | public float Height
81 | {
82 | get
83 | {
84 | if (this.ResizeMode == AspectMode.Expand)
85 | return this.viewport.Y;
86 |
87 | return this.height ?? this.viewport.Y;
88 | }
89 | set => this.height = value;
90 | }
91 |
92 | public float Zoom
93 | {
94 | get => 1 / this.Transform.Scale.X;
95 | set => this.Transform.Scale = new Vector2(1 / value,1 / value);
96 | }
97 |
98 | public float Rotation
99 | {
100 | get => this.Transform.Rotation;
101 | set => this.Transform.Rotation = value;
102 | }
103 |
104 |
105 | public Vector2 Position
106 | {
107 | get => this.Transform.AbsolutePosition;
108 | set => this.Transform.Position = value;
109 | }
110 |
111 | #endregion
112 |
113 | #region Methods
114 |
115 | public Rectangle GetBounds()
116 | {
117 | var pos = this.Transform.AbsolutePosition;
118 | var w = Width * this.Transform.AbsoluteScale.X;
119 | var h = Height * this.Transform.AbsoluteScale.Y;
120 | var s = Math.Max(w, h);
121 | var x = pos.X - (s / 2);
122 | var y = pos.Y - (s / 2);
123 | return new Rectangle((int)x, (int)y, (int)w, (int)h);
124 | }
125 |
126 | public void LoadContent()
127 | {
128 | this.Debug.LoadContent(device);
129 | }
130 |
131 | public void ToScreen(ref Vector2 worldPosition, out Vector2 screenPosition)
132 | {
133 | this.Transform.ToLocalPosition(ref worldPosition, out screenPosition);
134 | }
135 |
136 | public void ToWorld(ref Vector2 screenPosition, out Vector2 worldPosition)
137 | {
138 | this.Transform.ToAbsolutePosition(ref screenPosition, out worldPosition);
139 | }
140 |
141 | public void Update(GameTime time)
142 | {
143 | if(this.viewport.X != this.device.Viewport.Width || this.viewport.X != this.device.Viewport.Height)
144 | {
145 | this.viewport = new Vector2(this.device.Viewport.Width, this.device.Viewport.Height);
146 | this.UpdateOffset();
147 | }
148 |
149 | this.Debug.Update(time);
150 | }
151 |
152 | private void UpdateOffset()
153 | {
154 | var w = this.Width;
155 | var h = this.Height;
156 |
157 | switch (this.ResizeMode)
158 | {
159 | case AspectMode.FillStretch:
160 | this.viewportOffset.Scale = new Vector2(w / viewport.X, h / viewport.Y);
161 | w = this.viewport.X * this.viewportOffset.Scale.X;
162 | h = viewport.Y * this.viewportOffset.Scale.Y;
163 | break;
164 | case AspectMode.FillUniform:
165 | var size = Math.Min(w / viewport.X, h / viewport.Y);
166 | this.viewportOffset.Scale = new Vector2(size, size);
167 | w = viewport.X * this.viewportOffset.Scale.X;
168 | h = viewport.Y * this.viewportOffset.Scale.Y;
169 | break;
170 | }
171 |
172 | this.viewportOffset.Position = new Vector2(-w * 0.5f, -h * 0.5f);
173 | }
174 |
175 | public ICamera Clone()
176 | {
177 | var result = new Camera(this.device);
178 | result.width = this.width;
179 | result.height = this.height;
180 | result.aspectMode = this.aspectMode;
181 | result.Transform.Position = this.Transform.Position;
182 | result.Transform.Rotation = this.Transform.Rotation;
183 | result.Transform.Scale = this.Transform.Scale;
184 | result.UpdateOffset();
185 | return result;
186 | }
187 |
188 | #endregion
189 | }
190 | }
191 |
192 |
193 |
--------------------------------------------------------------------------------
/Sources/Comora/Comora.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {EF814457-2BEF-4D6E-BE2A-7E660EC8BBF6}
7 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
8 | Library
9 | Comora
10 | Comora
11 | v4.5
12 | Profile111
13 |
14 |
15 | true
16 | full
17 | false
18 | bin\Debug
19 | DEBUG;
20 | prompt
21 | 4
22 | false
23 | false
24 |
25 |
26 | true
27 | bin\Release
28 | prompt
29 | 4
30 | false
31 | false
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | ..\packages\MonoGame.Framework.Portable.3.6.0.1625\lib\portable-net45+win8+wpa81\MonoGame.Framework.dll
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Sources/Comora/Diagnostics/DebugLayer.cs:
--------------------------------------------------------------------------------
1 | namespace Comora.Diagnostics
2 | {
3 | using Microsoft.Xna.Framework;
4 | using Microsoft.Xna.Framework.Graphics;
5 |
6 | public class DebugLayer
7 | {
8 | public DebugLayer(Camera camera)
9 | {
10 | this.font = new PixelFont();
11 | this.FpsCounter = new FpsCounter(camera,font);
12 | this.Grid = new Grid(camera,font);
13 | }
14 |
15 | readonly PixelFont font;
16 |
17 | public bool IsVisible { get; set; }
18 |
19 | public IGrid Grid { get; }
20 |
21 | public IFpsCounter FpsCounter { get; }
22 |
23 | public Texture2D Pixel { get; private set; }
24 |
25 | public Color BackgroundColor { get; set; } = new Color(Color.Black, 0.2f);
26 |
27 | private GraphicsDevice device;
28 |
29 | public void LoadContent(GraphicsDevice device)
30 | {
31 | this.device = device;
32 | Pixel = new Texture2D(device, 1, 1);
33 | Pixel.SetData(new Color[] { Color.White });
34 | this.font.LoadContent(device);
35 | this.Grid.LoadContent(device);
36 | this.FpsCounter.LoadContent(device);
37 | }
38 |
39 | public void Update(GameTime gameTime)
40 | {
41 | this.FpsCounter.Update(gameTime);
42 | }
43 |
44 | public void Draw(SpriteBatch sb, Vector2 parralax)
45 | {
46 | if (this.IsVisible)
47 | {
48 | sb.Begin();
49 | sb.Draw(this.Pixel, new Rectangle(0, 0, this.device.Viewport.Width, this.device.Viewport.Height), BackgroundColor);
50 | sb.End();
51 |
52 | this.FpsCounter.Draw(sb);
53 | this.Grid.Draw(sb,parralax);
54 | this.FpsCounter.Draw(sb);
55 | }
56 | }
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/Sources/Comora/Diagnostics/FpsCounter.cs:
--------------------------------------------------------------------------------
1 | namespace Comora.Diagnostics
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Graphics;
7 |
8 | public class FpsCounter : IFpsCounter
9 | {
10 | public FpsCounter(Camera camera, PixelFont font, int maximumSamples = 100)
11 | {
12 | this.camera = camera;
13 | MaximumSamples = maximumSamples;
14 | this.font = font;
15 | }
16 |
17 | #region Fields
18 |
19 | private readonly PixelFont font;
20 |
21 | private Texture2D pixel;
22 |
23 | private readonly Camera camera;
24 |
25 | private readonly Queue sampleBuffer = new Queue();
26 |
27 | private static readonly Color bgColor = new Color(Color.Black, 0.5f);
28 |
29 | #endregion
30 |
31 | #region Properties
32 |
33 | public long TotalFrames { get; private set; }
34 |
35 | public float AverageFramesPerSecond { get; private set; }
36 |
37 | public float CurrentFramesPerSecond { get; private set; }
38 |
39 | public int MaximumSamples { get; }
40 |
41 | public bool IsVisible { get; set; } = true;
42 |
43 | #endregion
44 |
45 | #region Lifecycle
46 |
47 | public void LoadContent(GraphicsDevice device)
48 | {
49 | pixel = new Texture2D(device, 1, 1);
50 | pixel.SetData(new Color[] { Color.White });
51 | }
52 |
53 | public void Reset()
54 | {
55 | TotalFrames = 0;
56 | sampleBuffer.Clear();
57 | }
58 |
59 | public void Update(GameTime gameTime)
60 | {
61 | if (!IsVisible)
62 | return;
63 |
64 | var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
65 |
66 | CurrentFramesPerSecond = 1.0f / deltaTime;
67 |
68 | sampleBuffer.Enqueue(CurrentFramesPerSecond);
69 |
70 | if (sampleBuffer.Count > MaximumSamples)
71 | {
72 | sampleBuffer.Dequeue();
73 | AverageFramesPerSecond = sampleBuffer.Average(i => i);
74 | }
75 | else
76 | {
77 | AverageFramesPerSecond = CurrentFramesPerSecond;
78 | }
79 |
80 | TotalFrames++;
81 | }
82 |
83 | public void Draw(SpriteBatch sb)
84 | {
85 | if (!IsVisible)
86 | return;
87 |
88 | sb.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp);
89 |
90 | var message = ((int)this.CurrentFramesPerSecond).ToString();
91 | var s = this.font.Measure(message, 15);
92 |
93 | var width = s.X + 20;
94 | var bounds = new Rectangle((int)sb.GraphicsDevice.Viewport.Width - 20 - width, 20, width, 20 + s.Y);
95 |
96 | sb.Draw(this.pixel, bounds, bgColor);
97 | this.font.Draw(sb, new Vector2(bounds.X + 10, bounds.Y + 10),message , Color.White,15);
98 |
99 | sb.End();
100 | }
101 |
102 | #endregion
103 |
104 | }
105 | }
106 |
107 |
--------------------------------------------------------------------------------
/Sources/Comora/Diagnostics/Grid.cs:
--------------------------------------------------------------------------------
1 | namespace Comora.Diagnostics
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Graphics;
7 |
8 | public class Grid : IGrid
9 | {
10 | public class Unit
11 | {
12 | public double Value { get; set; }
13 |
14 | public double Width { get; set; }
15 |
16 | public Color Color { get; set; }
17 | }
18 |
19 | public Grid(ICamera camera, PixelFont font)
20 | {
21 | this.camera = camera;
22 | this.font = font;
23 | this.units = new List();
24 | }
25 |
26 | #region Fields
27 |
28 | private PixelFont font;
29 |
30 | private Texture2D pixel;
31 |
32 | private readonly ICamera camera;
33 |
34 | private List units;
35 |
36 | #endregion
37 |
38 | #region Units
39 |
40 | public void AddLines(double intervals, Color color, double width = 1)
41 | {
42 | this.units.Add(new Unit
43 | {
44 | Value = intervals,
45 | Width = width,
46 | Color = color
47 | });
48 | }
49 |
50 | public void RemoveLines()
51 | {
52 | this.units.Clear();
53 | }
54 |
55 | #endregion
56 |
57 | public bool IsVisible { get; set; } = true;
58 |
59 | #region Lifecycle
60 |
61 | public void LoadContent(GraphicsDevice device)
62 | {
63 | pixel = new Texture2D(device, 1, 1);
64 | pixel.SetData(new Color[] { Color.White });
65 | }
66 |
67 | public void Draw(SpriteBatch spriteBatch)
68 | {
69 | this.Draw(spriteBatch, Vector2.One);
70 | }
71 |
72 | Color bgColor = new Color(Color.Black, 0.5f);
73 |
74 | public void Draw(SpriteBatch spriteBatch, Vector2 parralax)
75 | {
76 | if (!this.IsVisible)
77 | return;
78 |
79 | if (this.pixel == null)
80 | throw new InvalidOperationException("The grid 'LoadContent' must be invoked prior to any 'Draw'.");
81 |
82 | var px = this.camera.Transform.AbsolutePosition.X;
83 | var py = this.camera.Transform.AbsolutePosition.Y;
84 |
85 | var bounds = this.camera.GetBounds();
86 |
87 | var w = bounds.Width;
88 | var h = bounds.Height;
89 |
90 | spriteBatch.Begin(this.camera);
91 |
92 | foreach (var unit in this.units)
93 | {
94 | var intervalX = unit.Value;
95 | var intervalY = unit.Value;
96 |
97 | var columns = 2 + (int)(w / intervalX);
98 | var rows = 2 + (int)(h / intervalY);
99 |
100 | var lw = w * intervalY;
101 | var lh = h * intervalX;
102 |
103 | var startX = ((int)(px / intervalX) * intervalX) - (1 + columns / 2) * intervalX;
104 | var startY = ((int)(py / intervalY) * intervalY) - (1 + rows / 2) * intervalY;
105 |
106 | for (int i = 0; i <= columns; i++)
107 | {
108 | var x = startX + i * intervalX;
109 | spriteBatch.Draw(this.pixel, new Rectangle((int)x, (int)startY, (int)(unit.Width), (int)lh), unit.Color);
110 | }
111 |
112 | for (int i = 0; i <= rows; i++)
113 | {
114 | var y = startY + i * intervalY;
115 | spriteBatch.Draw(this.pixel, new Rectangle((int)startX, (int)y, (int)lw, (int)(unit.Width)), unit.Color);
116 | }
117 | }
118 |
119 | spriteBatch.End();
120 |
121 | var message = $"{(int)px},{(int)py}\nANGLE: {this.camera.Transform.Rotation}\nZOOM: {this.camera.Zoom}";
122 |
123 | spriteBatch.Begin(SpriteSortMode.Deferred,null,SamplerState.PointClamp);
124 |
125 | var messageSize = this.font.Measure(message, 10);
126 |
127 | spriteBatch.Draw(this.pixel, new Rectangle(20, 20, messageSize.X + 20, messageSize.Y + 20), bgColor);
128 |
129 | this.font.Draw(spriteBatch, new Vector2(30, 30), message , Color.White, 10);
130 |
131 | spriteBatch.End();
132 | }
133 |
134 | #endregion
135 |
136 | }
137 | }
--------------------------------------------------------------------------------
/Sources/Comora/Diagnostics/IFpsCounter.cs:
--------------------------------------------------------------------------------
1 | namespace Comora.Diagnostics
2 | {
3 | using Microsoft.Xna.Framework;
4 | using Microsoft.Xna.Framework.Graphics;
5 |
6 | public interface IFpsCounter
7 | {
8 | bool IsVisible { get; set; }
9 |
10 | float CurrentFramesPerSecond { get; }
11 |
12 | void LoadContent(GraphicsDevice device);
13 |
14 | void Update(GameTime gameTime);
15 |
16 | void Draw(SpriteBatch sb);
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/Sources/Comora/Diagnostics/IGrid.cs:
--------------------------------------------------------------------------------
1 | namespace Comora.Diagnostics
2 | {
3 | using Microsoft.Xna.Framework;
4 | using Microsoft.Xna.Framework.Graphics;
5 |
6 | public interface IGrid
7 | {
8 | bool IsVisible { get; set; }
9 |
10 | void AddLines(double intervals, Color color, double width = 1);
11 |
12 | void RemoveLines();
13 |
14 | void LoadContent(GraphicsDevice device);
15 |
16 | void Draw(SpriteBatch spriteBatch);
17 |
18 | void Draw(SpriteBatch spriteBatch, Vector2 parralax);
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/Sources/Comora/Diagnostics/PixelFont.cs:
--------------------------------------------------------------------------------
1 | namespace Comora.Diagnostics
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Graphics;
7 |
8 | public class PixelFont
9 | {
10 | private readonly Dictionary Pixels = new Dictionary()
11 | {
12 | { '0', new short[] { 0, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 0 }},
13 | { '1', new short[] { 0, 1, 0, /**/ 1, 1, 0, /**/ 0, 1, 0, /**/ 0, 1, 0, /**/ 1, 1, 1 }},
14 | { '2', new short[] { 0, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 0, 0, 1, 0, /**/ 0, 1, 0, 0, /**/ 1, 1, 1, 1 }},
15 | { '3', new short[] { 1, 1, 1, 0, /**/ 0, 0, 0, 1, /**/ 0, 1, 1, 0, /**/ 0, 0, 0, 1, /**/ 1, 1, 1, 0 }},
16 | { '4', new short[] { 0, 0, 1, 1, /**/ 0, 1, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 1, /**/ 0, 0, 0, 1 }},
17 | { '5', new short[] { 1, 1, 1, 1, /**/ 1, 0, 0, 0, /**/ 1, 1, 1, 1, /**/ 0, 0, 0, 1, /**/ 1, 1, 1, 0 }},
18 | { '6', new short[] { 0, 1, 1, 0, /**/ 1, 0, 0, 0, /**/ 1, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 0 }},
19 | { '7', new short[] { 1, 1, 1, 1, /**/ 0, 0, 0, 1, /**/ 0, 0, 1, 0, /**/ 0, 0, 1, 0, /**/ 0, 0, 1, 0 }},
20 | { '8', new short[] { 0, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 0 }},
21 | { '9', new short[] { 0, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 1, /**/ 0, 0, 0, 1, /**/ 0, 1, 1, 0 }},
22 | { '-', new short[] { 0, 0, 0, /**/ 0, 0, 0, /**/ 1, 1, 1, /**/ 0, 0, 0, /**/ 0, 0, 0 }},
23 | { '+', new short[] { 0, 0, 0, /**/ 0, 1, 0, /**/ 1, 1, 1, /**/ 0, 1, 0, /**/ 0, 0, 0 }},
24 | { ':', new short[] { 0, 0, 0, /**/ 0, 1, 0, /**/ 0, 0, 0, /**/ 0, 1, 0, /**/ 0, 0, 0 }},
25 | { '(', new short[] { 0, 1, /**/ 1, 0, /**/ 1, 0, /**/ 1, 0, /**/ 0, 1 }},
26 | { ')', new short[] { 1, 0, /**/ 0, 1, /**/ 0, 1, /**/ 0, 1, /**/ 1, 0 }},
27 | { '.', new short[] { 0, 0, /**/ 0, 0, /**/ 0, 0, /**/ 0, 0, /**/ 1, 0 }},
28 | { ',', new short[] { 0, 0, /**/ 0, 0, /**/ 0, 0, /**/ 0, 1, /**/ 1, 0 }},
29 | { 'A', new short[] { 0, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 1, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1 }},
30 | { 'B', new short[] { 1, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 0 }},
31 | { 'C', new short[] { 0, 1, 1, 1, /**/ 1, 0, 0, 0, /**/ 1, 0, 0, 0, /**/ 1, 0, 0, 0, /**/ 0, 1, 1, 1 }},
32 | { 'D', new short[] { 1, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 0 }},
33 | { 'E', new short[] { 1, 1, 1, 1, /**/ 1, 0, 0, 0, /**/ 1, 1, 1, 1, /**/ 1, 0, 0, 0, /**/ 1, 1, 1, 1 }},
34 | { 'F', new short[] { 1, 1, 1, 1, /**/ 1, 0, 0, 0, /**/ 1, 1, 1, 1, /**/ 1, 0, 0, 0, /**/ 1, 0, 0, 0 }},
35 | { 'G', new short[] { 1, 1, 1, 1, /**/ 1, 0, 0, 0, /**/ 1, 0, 1, 1, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 1 }},
36 | { 'H', new short[] { 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 1, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1 }},
37 | { 'I', new short[] { 1, /**/ 1, /**/ 1, /**/ 1, /**/ 1 }},
38 | { 'J', new short[] { 0, 0, 0, 1, /**/ 0, 0, 0, 1, /**/ 0, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 0 }},
39 | { 'K', new short[] { 1, 0, 0, 1, /**/ 1, 0, 1, 0, /**/ 1, 1, 0, 0, /**/ 1, 0, 1, 0, /**/ 1, 0, 0, 1 }},
40 | { 'L', new short[] { 1, 0, 0, /**/ 1, 0, 0, /**/ 1, 0, 0, /**/ 1, 0, 0, /**/ 1, 1, 1 }},
41 | { 'M', new short[] { 1, 0, 0, 0, 1, /**/ 1, 1, 0, 1, 1, /**/ 1, 0, 1, 0, 1, /**/ 1, 0, 0, 0, 1, /**/ 1, 0, 0, 0, 1 }},
42 | { 'N', new short[] { 1, 0, 0, 1, /**/ 1, 1, 0, 1, /**/ 1, 0, 1, 1, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1 }},
43 | { 'O', new short[] { 0, 1, 1, 1, 0, /**/ 1, 0, 0, 0, 1, /**/ 1, 0, 0, 0, 1, /**/ 1, 0, 0, 0, 1, /**/ 0, 1, 1, 1, 0 }},
44 | { 'P', new short[] { 1, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 0, /**/ 1, 0, 0, 0, /**/ 1, 0, 0, 0 }},
45 | { 'Q', new short[] { 0, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 0, 1, 0, /**/ 0, 1, 0, 1 }},
46 | { 'R', new short[] { 1, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 0, /**/ 1, 0, 1, 0, /**/ 1, 0, 0, 1 }},
47 | { 'S', new short[] { 0, 1, 1, 1, /**/ 1, 0, 0, 0, /**/ 0, 1, 1, 0, /**/ 0, 0, 0, 1, /**/ 1, 1, 1, 0 }},
48 | { 'T', new short[] { 1, 1, 1, 1, 1, /**/ 0, 0, 1, 0, 0, /**/ 0, 0, 1, 0, 0, /**/ 0, 0, 1, 0, 0, /**/ 0, 0, 1, 0, 0 }},
49 | { 'U', new short[] { 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 0 }},
50 | { 'V', new short[] { 1, 0, 0, 0, 1, /**/ 1, 0, 0, 0, 1, /**/ 0, 1, 0, 1, 0, /**/ 0, 1, 0, 1, 0, /**/ 0, 0, 1, 0, 0 }},
51 | { 'W', new short[] { 1, 0, 1, 0, 1, /**/ 1, 0, 1, 0, 1, /**/ 1, 0, 1, 0, 1, /**/ 0, 1, 0, 1, 0, /**/ 0, 1, 0, 1, 0 }},
52 | { 'X', new short[] { 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 0, 1, 1, 0, /**/ 1, 0, 0, 1, /**/ 1, 0, 0, 1 }},
53 | { 'Y', new short[] { 1, 0, 0, 1, /**/ 1, 0, 0, 1, /**/ 1, 1, 1, 1, /**/ 0, 0, 0, 1, /**/ 1, 1, 1, 0 }},
54 | { 'Z', new short[] { 1, 1, 1, 1, /**/ 0, 0, 0, 1, /**/ 0, 1, 1, 0, /**/ 1, 0, 0, 0, /**/ 1, 1, 1, 1 }},
55 | };
56 |
57 | private const int LetterSpace = 1;
58 |
59 | private const int LineSpace = 3;
60 |
61 | private const int Space = 4;
62 |
63 | private Dictionary Textures;
64 |
65 | public void LoadContent(GraphicsDevice device)
66 | {
67 | if (this.Textures == null)
68 | {
69 | this.Textures = new Dictionary();
70 | foreach (var c in Pixels)
71 | {
72 | Textures[c.Key] = this.LoadCharacter(device, c.Key, c.Value);
73 | }
74 | }
75 | }
76 |
77 | private Texture2D LoadCharacter(GraphicsDevice device, char c, short[] pixels)
78 | {
79 | const int height = 5;
80 | var width = pixels.Length / height;
81 | var result = new Texture2D(device, width, height);
82 | var data = new Color[width * height];
83 | for (int pixel = 0; pixel < data.Length; pixel++)
84 | {
85 | data[pixel] = pixels[pixel] > 0 ? Color.White : Color.Transparent;
86 | }
87 | result.SetData(data);
88 | return result;
89 | }
90 |
91 | public Point Measure(string message, int fontSize = 10)
92 | {
93 | var scale = fontSize / 5.0f;
94 | var x = 0;
95 | var y = fontSize;
96 | var w = 0;
97 |
98 | foreach (var c in message)
99 | {
100 | if (c == ' ')
101 | {
102 | x += LetterSpace + Space;
103 | }
104 | else if (c == '\n')
105 | {
106 | y += LineSpace + fontSize;
107 | x = 0;
108 | }
109 | else
110 | {
111 | var texture = this.Textures[c];
112 | x += (int)((((x > 0) ? LetterSpace : 0) + texture.Width) * scale);
113 | }
114 |
115 | w = Math.Max(w, x);
116 | }
117 |
118 | return new Point((int)w, (int)y);
119 | }
120 |
121 |
122 | public void Draw(SpriteBatch spriteBatch, Vector2 position, string message, Color color, int fontSize = 10)
123 | {
124 | var scale = fontSize / 5.0f;
125 | var x = position.X;
126 | var y = position.Y;
127 |
128 | foreach (var c in message)
129 | {
130 | if (c == ' ')
131 | {
132 | x += LetterSpace + Space;
133 | }
134 | else if (c == '\n')
135 | {
136 | y += LineSpace + fontSize;
137 | x = position.X;
138 | }
139 | else
140 | {
141 | var texture = this.Textures[c];
142 | spriteBatch.Draw(texture, new Vector2(x, y), color: color, scale: Vector2.One * scale);
143 | x += (int)((((x > 0) ? LetterSpace : 0) + texture.Width) * scale);
144 | }
145 | }
146 | }
147 | }
148 | }
149 |
150 |
--------------------------------------------------------------------------------
/Sources/Comora/ICamera.cs:
--------------------------------------------------------------------------------
1 | namespace Comora
2 | {
3 | using Comora.Diagnostics;
4 | using Microsoft.Xna.Framework;
5 | using Microsoft.Xna.Framework.Graphics;
6 | using Transform;
7 |
8 | ///
9 | /// Represents a player camera.
10 | ///
11 | public interface ICamera
12 | {
13 | #region Properties
14 |
15 | ///
16 | /// Gets or sets the way the content fits the screen.
17 | ///
18 | /// The resize mode.
19 | AspectMode ResizeMode { get; set; }
20 |
21 | ///
22 | /// Gets or sets the width of the screen.
23 | ///
24 | /// The width.
25 | float Width { get; set; }
26 |
27 | ///
28 | /// Gets or sets the height of the screen.
29 | ///
30 | /// The height.
31 | float Height { get; set; }
32 |
33 | ///
34 | /// Gets or sets the transform of the camera (position, scale and rotation).
35 | ///
36 | /// The transform.
37 | Transform2D Transform { get; set; }
38 |
39 | Transform2D ViewportOffset { get; }
40 |
41 | ///
42 | /// Gets or sets the zoom level.
43 | ///
44 | /// The zoom.
45 | float Zoom { get; set; }
46 |
47 | ///
48 | /// Gets or sets the rotation.
49 | ///
50 | /// The rotation.
51 | float Rotation { get; set; }
52 |
53 | ///
54 | /// Gets or sets the position.
55 | ///
56 | /// The position.
57 | Vector2 Position { get; set; }
58 |
59 | ///
60 | /// Gets the layer that displays debugging information.
61 | ///
62 | /// The debug.
63 | DebugLayer Debug { get; }
64 |
65 | #endregion
66 |
67 | #region Methods
68 |
69 | Rectangle GetBounds();
70 |
71 | #region Conversions
72 |
73 | ///
74 | /// Converts absolute world to the screen relative coordinates.
75 | ///
76 | /// The screen coordinates.
77 | /// A world absolute position.
78 | /// The local screen position.
79 | void ToScreen(ref Vector2 worldPosition, out Vector2 screenPosition);
80 |
81 | ///
82 | /// Converts relative coordinates to absolute world coordinate.
83 | ///
84 | /// The world coordinates.
85 | /// The local screen position.
86 | /// A world absolute position.
87 | void ToWorld(ref Vector2 screenPosition, out Vector2 worldPosition);
88 |
89 | #endregion
90 |
91 | #region Lifecycle
92 |
93 | ///
94 | /// Loads the content.
95 | ///
96 | /// Device.
97 | void LoadContent();
98 |
99 | void Update(GameTime time);
100 |
101 | #endregion
102 |
103 | ICamera Clone();
104 |
105 | #endregion
106 | }
107 | }
108 |
109 |
--------------------------------------------------------------------------------
/Sources/Comora/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle("Comora")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("")]
12 | [assembly: AssemblyCopyright("Aloïs Deniel")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | [assembly: AssemblyVersion("0.5.0")]
21 |
22 | // The following attributes are used to specify the signing key for the assembly,
23 | // if desired. See the Mono documentation for more information about signing.
24 |
25 | //[assembly: AssemblyDelaySign(false)]
26 | //[assembly: AssemblyKeyFile("")]
27 |
28 |
--------------------------------------------------------------------------------
/Sources/Comora/SpriteBatchExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Comora
2 | {
3 | using Comora.Diagnostics;
4 | using Microsoft.Xna.Framework;
5 | using Microsoft.Xna.Framework.Graphics;
6 |
7 | public static class SpriteBatchExtensions
8 | {
9 | public static void Begin(this SpriteBatch spriteBatch, ICamera camera, SpriteSortMode sortMode = SpriteSortMode.Deferred, BlendState blendState = null, SamplerState samplerState = null, DepthStencilState depthStencilState = null, RasterizerState rasterizerState = null, Effect effect = null)
10 | {
11 | spriteBatch.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, camera.ViewportOffset.InvertAbsolute);
12 | }
13 |
14 | public static void Draw(this SpriteBatch spriteBatch, IGrid grid)
15 | {
16 | grid.Draw(spriteBatch);
17 | }
18 |
19 | public static void Draw(this SpriteBatch spriteBatch, DebugLayer grid)
20 | {
21 | grid.Draw(spriteBatch, Vector2.One);
22 | }
23 |
24 | public static void Draw(this SpriteBatch spriteBatch, DebugLayer grid, Vector2 parralax)
25 | {
26 | grid.Draw(spriteBatch,parralax);
27 | }
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Sources/Comora/Transform.cs:
--------------------------------------------------------------------------------
1 | // INCLUDED FILE, DO NOT MODIFY IT
2 | // (from nuget package 'Transform.Sources')
3 |
4 |
5 | namespace Transform
6 | {
7 | using System;
8 | using Microsoft.Xna.Framework;
9 |
10 | public class Acceleration2D
11 | {
12 | public Acceleration2D(Velocity2D velocity)
13 | {
14 | this.Velocity = velocity ?? throw new ArgumentException(nameof(velocity));
15 | }
16 |
17 | public Transform2D Transform => this.Velocity.Transform;
18 |
19 | public Velocity2D Velocity { get; }
20 |
21 | public Vector2 Position { get; set; }
22 |
23 | public Vector2 Scale { get; set; }
24 |
25 | public float Rotation { get; set; }
26 |
27 | public void Update(GameTime time)
28 | {
29 | var delta = (float)time.ElapsedGameTime.TotalSeconds;
30 | this.Velocity.Position += this.Position * delta;
31 | this.Velocity.Scale += this.Scale * delta;
32 | this.Velocity.Rotation += this.Rotation * delta;
33 |
34 | this.Velocity.Update(time);
35 | }
36 | }
37 | }
38 |
39 | namespace Transform
40 | {
41 | using System;
42 |
43 | public enum Ease
44 | {
45 | InOut,
46 | In,
47 | Out,
48 | ElasticIn,
49 | ElasticOut,
50 | ElasticInOut,
51 | }
52 |
53 | public static class EaseFunctions
54 | {
55 | private static readonly float PI_2 = (float)(Math.PI / 2);
56 |
57 | public static float In(float t) => t * t;
58 |
59 | public static float Out(float t) => t * (2 - t);
60 |
61 | public static float InOut(float t) => t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
62 |
63 | public static float ElasticIn(float t) => (float)(Math.Sin(13 * PI_2 * t) * Math.Pow(2, 10 * (t - 1)));
64 |
65 | public static float ElasticOut(float t) => (float)(Math.Sin(-13 * PI_2 * (t + 1)) * Math.Pow(2, -10 * t) + 1);
66 |
67 | public static float ElasticInOut(float t)
68 | {
69 | if (t < 0.5f)
70 | {
71 | return (float)(0.5 * Math.Sin(13 * PI_2 * (2 * t)) * Math.Pow(2, 10 * ((2 * t) - 1)));
72 | }
73 | return (float)(0.5 * (Math.Sin(-13 * PI_2 * ((2 * t - 1) + 1)) * Math.Pow(2, -10 * (2 * t - 1)) + 2));
74 | }
75 |
76 | public static Func Get(Ease ease)
77 | {
78 | switch (ease)
79 | {
80 | case Ease.In: return In;
81 | case Ease.Out: return Out;
82 | case Ease.InOut: return InOut;
83 | case Ease.ElasticIn: return ElasticIn;
84 | case Ease.ElasticOut: return ElasticOut;
85 | case Ease.ElasticInOut: return ElasticInOut;
86 | }
87 |
88 | throw new NotSupportedException();
89 | }
90 | }
91 | }
92 |
93 | namespace Transform
94 | {
95 | using Microsoft.Xna.Framework;
96 |
97 | public static class Extensions
98 | {
99 | public static Transform2D ToTransform(this Vector2 position)
100 | {
101 | return new Transform2D()
102 | {
103 | Position = position,
104 | };
105 | }
106 |
107 | public static Velocity2D WithVelocity(this Transform2D transform)
108 | {
109 | return new Velocity2D(transform);
110 | }
111 |
112 | public static Acceleration2D WithAcceleration(this Velocity2D velocity)
113 | {
114 | return new Acceleration2D(velocity);
115 | }
116 | }
117 | }
118 |
119 | namespace Transform
120 | {
121 | using Microsoft.Xna.Framework;
122 | using System.Collections.Generic;
123 |
124 | public class Transform2D
125 | {
126 | #region Constructors
127 |
128 | public Transform2D()
129 | {
130 | this.Position = Vector2.Zero;
131 | this.Rotation = 0;
132 | this.Scale = Vector2.One;
133 | }
134 |
135 | #endregion
136 |
137 | #region Fields
138 |
139 | private Transform2D parent;
140 |
141 | private List children = new List();
142 |
143 | private Matrix absolute, invertAbsolute, local;
144 |
145 | private float localRotation, absoluteRotation;
146 |
147 | private Vector2 localScale, absoluteScale, localPosition, absolutePosition;
148 |
149 | private bool needsAbsoluteUpdate = true, needsLocalUpdate = true;
150 |
151 | #endregion
152 |
153 | #region Properties
154 |
155 | ///
156 | /// Gets or sets the parent transform.
157 | ///
158 | /// The parent.
159 | public Transform2D Parent
160 | {
161 | get => this.parent;
162 | set
163 | {
164 | if(this.parent != value)
165 | {
166 | if (this.parent != null)
167 | this.parent.children.Remove(this);
168 |
169 | this.parent = value;
170 |
171 | if (this.parent != null)
172 | this.parent.children.Add(this);
173 |
174 | this.SetNeedsAbsoluteUpdate();
175 | }
176 | }
177 | }
178 |
179 | ///
180 | /// Gets all the children transform.
181 | ///
182 | /// The children.
183 | public IEnumerable Children => this.children;
184 |
185 | ///
186 | /// Gets the absolute rotation.
187 | ///
188 | /// The absolute rotation.
189 | public float AbsoluteRotation => this.UpdateAbsoluteAndGet(ref this.absoluteRotation);
190 |
191 | ///
192 | /// Gets the absolute scale.
193 | ///
194 | /// The absolute scale.
195 | public Vector2 AbsoluteScale => this.UpdateAbsoluteAndGet(ref this.absoluteScale);
196 |
197 | ///
198 | /// Gets the absolute position.
199 | ///
200 | /// The absolute position.
201 | public Vector2 AbsolutePosition => this.UpdateAbsoluteAndGet(ref this.absolutePosition);
202 |
203 | ///
204 | /// Gets or sets the rotation (relative to the parent, absolute if no parent).
205 | ///
206 | /// The rotation.
207 | public float Rotation
208 | {
209 | get => this.localRotation;
210 | set
211 | {
212 | if (this.localRotation != value)
213 | {
214 | this.localRotation = value;
215 | this.SetNeedsLocalUpdate();
216 | }
217 | }
218 | }
219 |
220 | ///
221 | /// Gets or sets the position (relative to the parent, absolute if no parent).
222 | ///
223 | /// The position.
224 | public Vector2 Position
225 | {
226 | get => this.localPosition;
227 | set
228 | {
229 | if(this.localPosition != value)
230 | {
231 | this.localPosition = value;
232 | this.SetNeedsLocalUpdate();
233 | }
234 | }
235 | }
236 |
237 | ///
238 | /// Gets or sets the scale (relative to the parent, absolute if no parent).
239 | ///
240 | /// The scale.
241 | public Vector2 Scale
242 | {
243 | get => this.localScale;
244 | set
245 | {
246 | if (this.localScale != value)
247 | {
248 | this.localScale = value;
249 | this.SetNeedsLocalUpdate();
250 | }
251 | }
252 | }
253 |
254 | ///
255 | /// Gets the matrix representing the local transform.
256 | ///
257 | /// The relative matrix.
258 | public Matrix Local => this.UpdateLocalAndGet(ref this.absolute);
259 |
260 | ///
261 | /// Gets the matrix representing the absolute transform.
262 | ///
263 | /// The absolute matrix.
264 | public Matrix Absolute => this.UpdateAbsoluteAndGet(ref this.absolute);
265 |
266 | ///
267 | /// Gets the matrix representing the invert of the absolute transform.
268 | ///
269 | /// The absolute matrix.
270 | public Matrix InvertAbsolute => this.UpdateAbsoluteAndGet(ref this.invertAbsolute);
271 |
272 | #endregion
273 |
274 | #region Methods
275 |
276 | public void ToLocalPosition(ref Vector2 absolute, out Vector2 local)
277 | {
278 | Vector2.Transform(ref absolute, ref this.invertAbsolute, out local);
279 | }
280 |
281 | public void ToAbsolutePosition(ref Vector2 local, out Vector2 absolute)
282 | {
283 | Vector2.Transform(ref local, ref this.absolute, out absolute);
284 | }
285 |
286 | public Vector2 ToLocalPosition(Vector2 absolute)
287 | {
288 | Vector2 result;
289 | ToLocalPosition(ref absolute, out result);
290 | return result;
291 | }
292 |
293 | public Vector2 ToAbsolutePosition(Vector2 local)
294 | {
295 | Vector2 result;
296 | ToAbsolutePosition(ref local, out result);
297 | return result;
298 | }
299 |
300 | private void SetNeedsLocalUpdate()
301 | {
302 | this.needsLocalUpdate = true;
303 | this.SetNeedsAbsoluteUpdate();
304 | }
305 |
306 | private void SetNeedsAbsoluteUpdate()
307 | {
308 | this.needsAbsoluteUpdate = true;
309 |
310 | foreach (var child in this.children)
311 | {
312 | child.SetNeedsAbsoluteUpdate();
313 | }
314 | }
315 |
316 | private void UpdateLocal()
317 | {
318 | var result = Matrix.CreateScale(this.Scale.X, this.Scale.Y, 1);
319 | result *= Matrix.CreateRotationZ(this.Rotation);
320 | result *= Matrix.CreateTranslation(this.Position.X, this.Position.Y, 0);
321 | this.local = result;
322 |
323 | this.needsLocalUpdate = false;
324 | }
325 |
326 | private void UpdateAbsolute()
327 | {
328 | if (this.Parent == null)
329 | {
330 | this.absolute = this.local;
331 | this.absoluteScale = this.localScale;
332 | this.absoluteRotation = this.localRotation;
333 | this.absolutePosition = this.localPosition;
334 | }
335 | else
336 | {
337 | var parentAbsolute = this.Parent.Absolute;
338 | Matrix.Multiply(ref this.local, ref parentAbsolute, out this.absolute);
339 | this.absoluteScale = this.Parent.AbsoluteScale * this.Scale;
340 | this.absoluteRotation = this.Parent.AbsoluteRotation + this.Rotation;
341 | this.absolutePosition = Vector2.Zero;
342 | this.ToAbsolutePosition(ref this.absolutePosition, out this.absolutePosition);
343 | }
344 |
345 | Matrix.Invert(ref this.absolute, out this.invertAbsolute);
346 |
347 | this.needsAbsoluteUpdate = false;
348 | }
349 |
350 | private T UpdateLocalAndGet(ref T field)
351 | {
352 | if (this.needsLocalUpdate)
353 | {
354 | this.UpdateLocal();
355 | }
356 |
357 | return field;
358 | }
359 |
360 | private T UpdateAbsoluteAndGet(ref T field)
361 | {
362 | if (this.needsLocalUpdate)
363 | {
364 | this.UpdateLocal();
365 | }
366 |
367 | if (this.needsAbsoluteUpdate)
368 | {
369 | this.UpdateAbsolute();
370 | }
371 |
372 | return field;
373 | }
374 |
375 | #endregion
376 |
377 | }
378 | }
379 |
380 | namespace Transform
381 | {
382 | using System;
383 | using Microsoft.Xna.Framework;
384 |
385 | public class Velocity2D
386 | {
387 | public Velocity2D(Transform2D transform)
388 | {
389 | this.Transform = transform ?? throw new ArgumentException(nameof(transform));
390 | }
391 |
392 | public Transform2D Transform { get; }
393 |
394 | public Vector2 Position { get; set; }
395 |
396 | public Vector2 Scale { get; set; }
397 |
398 | public float Rotation { get; set; }
399 |
400 | public void Update(GameTime time)
401 | {
402 | var delta = (float)time.ElapsedGameTime.TotalSeconds;
403 | this.Transform.Position += this.Position * delta;
404 | this.Transform.Scale += this.Scale * delta;
405 | this.Transform.Rotation += this.Rotation * delta;
406 | }
407 | }
408 | }
409 |
410 | namespace Transform
411 | {
412 | using System;
413 | using Microsoft.Xna.Framework;
414 |
415 | public class Delay : ITween
416 | {
417 | public Delay(TimeSpan duration)
418 | {
419 | this.Duration = duration;
420 | }
421 |
422 | public TimeSpan Time { get; private set; }
423 |
424 | public TimeSpan Duration { get; }
425 |
426 | public bool IsFinished { get; private set; }
427 |
428 | public void Reset()
429 | {
430 | this.IsFinished = false;
431 | this.Time = TimeSpan.Zero;
432 | }
433 |
434 | public bool Update(GameTime time)
435 | {
436 | if (!this.IsFinished)
437 | {
438 | var delta = (float)time.ElapsedGameTime.TotalSeconds;
439 | this.Time += time.ElapsedGameTime;
440 | this.IsFinished = (this.Time >= this.Duration);
441 | }
442 |
443 | return this.IsFinished;
444 | }
445 | }
446 | }
447 |
448 | namespace Transform
449 | {
450 | using System;
451 | using Microsoft.Xna.Framework;
452 |
453 | public interface ITween
454 | {
455 | TimeSpan Time { get; }
456 |
457 | TimeSpan Duration { get; }
458 |
459 | bool IsFinished { get; }
460 |
461 | void Reset();
462 |
463 | bool Update(GameTime time);
464 | }
465 | }
466 |
467 | namespace Transform
468 | {
469 | using System;
470 | using System.Linq;
471 | using Microsoft.Xna.Framework;
472 |
473 | public class Parallel : ITween
474 | {
475 | public Parallel(params ITween[] tweens)
476 | {
477 | this.tweens = tweens.ToArray();
478 | }
479 |
480 | private ITween[] tweens;
481 |
482 | public TimeSpan Time => this.tweens.Max(x => x.Time);
483 |
484 | public TimeSpan Duration => this.tweens.Max(x => x.Duration);
485 |
486 | public bool IsFinished => this.tweens.All(x => x.IsFinished);
487 |
488 | public void Reset()
489 | {
490 | foreach (var tween in this.tweens)
491 | {
492 | tween.Reset();
493 | }
494 | }
495 |
496 |
497 | public bool Update(GameTime time)
498 | {
499 | foreach (var tween in this.tweens)
500 | {
501 | if(!tween.IsFinished)
502 | {
503 | tween.Update(time);
504 | }
505 | }
506 |
507 | return this.IsFinished;
508 | }
509 | }
510 | }
511 |
512 | namespace Transform
513 | {
514 | using System;
515 | using Microsoft.Xna.Framework;
516 |
517 | public class Repeat : ITween
518 | {
519 | public Repeat(ITween tween, int times = -1)
520 | {
521 | this.tween = tween;
522 | this.RepeatTimes = times;
523 | }
524 |
525 | #region Fields
526 |
527 | private ITween tween;
528 |
529 | private int current;
530 |
531 | #endregion
532 |
533 | public TimeSpan Time { get; set; }
534 |
535 | public int RepeatTimes { get; }
536 |
537 | public TimeSpan Duration => TimeSpan.MaxValue;
538 |
539 | public bool IsFinished => RepeatTimes > 0 && current >= RepeatTimes;
540 |
541 | public void Reset()
542 | {
543 | current = 0;
544 | tween.Reset();
545 | }
546 |
547 | public bool Update(GameTime time)
548 | {
549 | var isFinished = this.IsFinished;
550 | if (!isFinished)
551 | {
552 | if(tween.Update(time))
553 | {
554 | current++;
555 |
556 | isFinished = this.IsFinished;
557 | if(!isFinished)
558 | {
559 | tween.Reset();
560 | }
561 | }
562 |
563 | }
564 |
565 | return isFinished;
566 | }
567 | }
568 | }
569 |
570 | namespace Transform
571 | {
572 | using System;
573 | using System.Linq;
574 | using Microsoft.Xna.Framework;
575 |
576 | public class Sequence : ITween
577 | {
578 | public Sequence(params ITween[] tweens)
579 | {
580 | this.tweens = tweens.ToArray();
581 | }
582 |
583 | private ITween[] tweens;
584 |
585 | public TimeSpan Time
586 | {
587 | get
588 | {
589 | var result = TimeSpan.Zero;
590 |
591 | for (int i = 0; i < this.tweens.Length; i++)
592 | {
593 | var tween = this.tweens[i];
594 |
595 | if(i == current)
596 | return result + tween.Time;
597 |
598 | result += tween.Duration;
599 | }
600 |
601 | return result;
602 | }
603 | }
604 |
605 | public TimeSpan Duration => new TimeSpan(tweens.Sum(x => x.Duration.Ticks));
606 |
607 | public bool IsFinished { get; private set; }
608 |
609 | public void Reset()
610 | {
611 | foreach (var tween in this.tweens)
612 | {
613 | tween.Reset();
614 | }
615 |
616 | this.current = 0;
617 | this.IsFinished = false;
618 | }
619 |
620 | private int current;
621 |
622 | public bool Update(GameTime time)
623 | {
624 | if (!this.IsFinished)
625 | {
626 | var tween = this.tweens[current];
627 |
628 | if(tween.Update(time))
629 | {
630 | current++;
631 | }
632 | this.IsFinished = current >= tweens.Length;
633 | }
634 |
635 | return this.IsFinished;
636 | }
637 | }
638 | }
639 |
640 | namespace Transform
641 | {
642 | using System;
643 | using Microsoft.Xna.Framework;
644 |
645 | public class Tween2D : ITween
646 | {
647 | public Tween2D(TimeSpan duration, Transform2D transform, Transform2D to, Ease ease)
648 | : this(duration, transform, new Transform2D()
649 | {
650 | Position = transform.Position,
651 | Rotation = transform.Rotation,
652 | Scale = transform.Scale,
653 | }, to, EaseFunctions.Get(ease))
654 | {
655 | }
656 |
657 | public Tween2D(TimeSpan duration, Transform2D transform, Transform2D from, Transform2D to, Ease ease)
658 | :this(duration, transform, from, to, EaseFunctions.Get(ease))
659 | {
660 | }
661 |
662 | public Tween2D(TimeSpan duration, Transform2D transform, Transform2D from, Transform2D to, Func ease)
663 | {
664 | this.Duration = duration;
665 | this.Transform = transform;
666 | this.From = from;
667 | this.To = to;
668 | this.Ease = Ease;
669 | this.easeFunction = ease;
670 | }
671 |
672 | private Func easeFunction;
673 |
674 | public TimeSpan Time { get; private set; }
675 |
676 | public TimeSpan Duration { get; }
677 |
678 | public Transform2D Transform { get; }
679 |
680 | public Transform2D From { get; }
681 |
682 | public Transform2D To { get; }
683 |
684 | public bool IsRevert { get; }
685 |
686 | public Ease Ease { get; }
687 |
688 | public bool IsFinished { get; private set; }
689 |
690 | public void Reset()
691 | {
692 | this.IsFinished = false;
693 | this.Time = TimeSpan.Zero;
694 |
695 | Transform.Position = From.Position;
696 | Transform.Scale = From.Scale;
697 | Transform.Rotation = From.Rotation;
698 | }
699 |
700 | public bool Update(GameTime time)
701 | {
702 | if(!this.IsFinished)
703 | {
704 | var delta = (float)time.ElapsedGameTime.TotalSeconds;
705 |
706 | this.Time += time.ElapsedGameTime;
707 |
708 | var t = Math.Max(0, Math.Min(1, (float)(this.Time.TotalMilliseconds / this.Duration.TotalMilliseconds)));
709 |
710 | var amount = easeFunction(t);
711 |
712 | Transform.Position = From.Position + (To.Position - From.Position) * amount;
713 | Transform.Scale = From.Scale + (To.Scale - From.Scale) * amount;
714 | Transform.Rotation = From.Rotation + (To.Rotation - From.Rotation) * amount;
715 |
716 | this.IsFinished = (t >= 1);
717 | }
718 |
719 | return this.IsFinished;
720 | }
721 | }
722 | }
723 |
724 |
--------------------------------------------------------------------------------
/Sources/Comora/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------