| 1 | # .skip. XNA is not commonly installed. other tests cover these features |
|---|
| 2 | # .require. dotnet |
|---|
| 3 | # .compile-only. |
|---|
| 4 | """ |
|---|
| 5 | xna.cobra |
|---|
| 6 | |
|---|
| 7 | This sample shows basic XNA use. |
|---|
| 8 | |
|---|
| 9 | XNA is Microsoft's .NET-based API for game programming on Windows, Xbox and other Microsoft devices. |
|---|
| 10 | |
|---|
| 11 | To use this sample: |
|---|
| 12 | * Download and install XNA Game Studio 3.1 |
|---|
| 13 | * Correct the XNA paths below if needed. |
|---|
| 14 | * Run with: cobra 700-XNA.cobra |
|---|
| 15 | * Use the arrow keys to move the man. |
|---|
| 16 | * You can hold more than one arrow key down at a time. |
|---|
| 17 | |
|---|
| 18 | If you don't see a gingerbread man and a dog, or you get errors about urls or images, then adjust |
|---|
| 19 | the `_getImages` method or supply your own man.png and dog.png. |
|---|
| 20 | |
|---|
| 21 | You can also run with "cobra -d ..." for debugging symbols and "cobra -turbo ..." |
|---|
| 22 | for maximum speed (and minimum checks). |
|---|
| 23 | |
|---|
| 24 | You can search the web for more information about XNA. |
|---|
| 25 | |
|---|
| 26 | For a cross platform alternative see http://cobra-language.com/trac/cobra/wiki/SFMLFramework |
|---|
| 27 | |
|---|
| 28 | Credit: Kurper, CobraCommander |
|---|
| 29 | """ |
|---|
| 30 | |
|---|
| 31 | # A typical directory for XNA follows. Correct if needed for your system. |
|---|
| 32 | @args -lib:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Windows\x86" |
|---|
| 33 | |
|---|
| 34 | # 64-bit Windows is a little different: |
|---|
| 35 | @args -lib:"C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v3.1\References\Windows\x86" |
|---|
| 36 | |
|---|
| 37 | @ref 'Microsoft.Xna.Framework' |
|---|
| 38 | @ref 'Microsoft.Xna.Framework.Game' |
|---|
| 39 | @ref 'System.Drawing' |
|---|
| 40 | |
|---|
| 41 | @number float32 |
|---|
| 42 | |
|---|
| 43 | use System.IO |
|---|
| 44 | use System.Net |
|---|
| 45 | use Microsoft.Xna.Framework |
|---|
| 46 | use Microsoft.Xna.Framework.Graphics |
|---|
| 47 | use Microsoft.Xna.Framework.Input |
|---|
| 48 | |
|---|
| 49 | |
|---|
| 50 | class Sprite |
|---|
| 51 | |
|---|
| 52 | var _texture as Texture2D |
|---|
| 53 | var _batch as SpriteBatch |
|---|
| 54 | var _pos as Vector2 |
|---|
| 55 | |
|---|
| 56 | cue init(texture as Texture2D, batch as SpriteBatch, pos as Vector2) |
|---|
| 57 | base.init |
|---|
| 58 | _texture, _batch, _pos = texture, batch, pos |
|---|
| 59 | |
|---|
| 60 | def draw(sb as SpriteBatch) |
|---|
| 61 | """ Draw this sprite on the screen. """ |
|---|
| 62 | sb.draw(_texture, _pos, Color.white) |
|---|
| 63 | |
|---|
| 64 | get x as number |
|---|
| 65 | return _pos.x |
|---|
| 66 | |
|---|
| 67 | get y as number |
|---|
| 68 | return _pos.y |
|---|
| 69 | |
|---|
| 70 | def move(dx as number, dy as number) |
|---|
| 71 | """ Moves this sprite dx pixels to the right and dy pixels down. """ |
|---|
| 72 | _pos.x += dx |
|---|
| 73 | _pos.y += dy |
|---|
| 74 | |
|---|
| 75 | |
|---|
| 76 | class MyGame inherits Game |
|---|
| 77 | |
|---|
| 78 | var _tick as uint |
|---|
| 79 | var _graphics as GraphicsDeviceManager |
|---|
| 80 | var _spriteBatch as SpriteBatch? |
|---|
| 81 | var _sprites as IList<of Sprite> |
|---|
| 82 | |
|---|
| 83 | cue init |
|---|
| 84 | base.init |
|---|
| 85 | _graphics = GraphicsDeviceManager(this) |
|---|
| 86 | _sprites = List<of Sprite>() |
|---|
| 87 | _getImages |
|---|
| 88 | |
|---|
| 89 | def _getImages |
|---|
| 90 | imgTools, maxWidth = ImageTools(), 128 |
|---|
| 91 | |
|---|
| 92 | url = 'http://icons.iconseeker.com/png/fullsize/the-real-christmas-05/gingerbread-man.png' |
|---|
| 93 | imgTools.fetchImage('man.png', url) |
|---|
| 94 | imgTools.constrainImageWidth('man.png', maxWidth) |
|---|
| 95 | |
|---|
| 96 | url = 'http://www.capek9cardio.com/wp-content/uploads/2009/05/bad-dog.png' |
|---|
| 97 | imgTools.fetchImage('dog.png', url) |
|---|
| 98 | imgTools.constrainImageWidth('dog.png', maxWidth) |
|---|
| 99 | |
|---|
| 100 | def loadContent is override, protected |
|---|
| 101 | _spriteBatch = SpriteBatch(_graphics.graphicsDevice) |
|---|
| 102 | # The canonical XNA way to do this is a content pipeline, but doing that |
|---|
| 103 | # without Visual Studio is a major pain. Instead, load from files directly. |
|---|
| 104 | # This is probably somewhat less efficient, but it shouldn't make a big |
|---|
| 105 | # difference unless you have a LOT of sprites. |
|---|
| 106 | man = Texture2D.fromFile(_graphics.graphicsDevice, 'man.png') |
|---|
| 107 | dog = Texture2D.fromFile(_graphics.graphicsDevice, 'dog.png') |
|---|
| 108 | # Create a few sprites at various positions. |
|---|
| 109 | _sprites.add(Sprite(man, _spriteBatch, Vector2(0, 0))) |
|---|
| 110 | _sprites.add(Sprite(dog, _spriteBatch, Vector2(man.width*2, 0))) |
|---|
| 111 | |
|---|
| 112 | def unloadContent is override, protected |
|---|
| 113 | # If you have any unloading to do, I guess you should put it here. |
|---|
| 114 | # We don't, or at least we're blissfully unaware if we do. |
|---|
| 115 | pass |
|---|
| 116 | |
|---|
| 117 | def update(gameTime as GameTime?) is override, protected |
|---|
| 118 | base.update(gameTime) |
|---|
| 119 | # Move the player's sprite depending on keyboard input. |
|---|
| 120 | # We just define the first thing in _sprites to be the player's |
|---|
| 121 | # and the second to be the dog. |
|---|
| 122 | # A real game would do something a bit more robust and organized. |
|---|
| 123 | assert _sprites.count |
|---|
| 124 | _tick += 1 |
|---|
| 125 | player = _sprites[0] |
|---|
| 126 | keys = Keyboard.getState |
|---|
| 127 | if keys.isKeyDown(Keys.Left), player.move(-1, 0) |
|---|
| 128 | if keys.isKeyDown(Keys.Right), player.move(1, 0) |
|---|
| 129 | if keys.isKeyDown(Keys.Up), player.move(0, -1) |
|---|
| 130 | if keys.isKeyDown(Keys.Down), player.move(0, 1) |
|---|
| 131 | # dog |
|---|
| 132 | if _tick % 2 == 0 |
|---|
| 133 | dog = _sprites[1] |
|---|
| 134 | if dog.x > player.x, dog.move(-1, 0) |
|---|
| 135 | else if dog.x < player.x, dog.move(1, 0) |
|---|
| 136 | if dog.y > player.y, dog.move(0, -1) |
|---|
| 137 | else if dog.y < player.y, dog.move(0, 1) |
|---|
| 138 | |
|---|
| 139 | def draw(gameTime as GameTime?) is override, protected |
|---|
| 140 | _graphics.graphicsDevice.clear(Color.cornflowerBlue) |
|---|
| 141 | batch = _spriteBatch to ! |
|---|
| 142 | batch.begin |
|---|
| 143 | for sprite in _sprites, sprite.draw(batch) |
|---|
| 144 | batch.end |
|---|
| 145 | base.draw(gameTime) |
|---|
| 146 | |
|---|
| 147 | def main |
|---|
| 148 | .run |
|---|
| 149 | |
|---|
| 150 | |
|---|
| 151 | class ImageTools |
|---|
| 152 | """ |
|---|
| 153 | Fetches images from URLs and can size them down to a max constrained width. |
|---|
| 154 | Uses System.Net and System.Drawing to accomplish these. |
|---|
| 155 | """ |
|---|
| 156 | |
|---|
| 157 | def fetchImage(fileName as String, url as String) |
|---|
| 158 | if File.exists(fileName), return |
|---|
| 159 | print 'Fetching a [fileName] from the web...' |
|---|
| 160 | System.Net.WebClient().downloadFile(url, fileName) |
|---|
| 161 | |
|---|
| 162 | def constrainImageWidth(fileName as String, width as int) |
|---|
| 163 | # A `use System.Drawing` above would expose two Color types, two Graphics types, etc. |
|---|
| 164 | # due to XNA having these things too. So instead this method uses fully qualified type names |
|---|
| 165 | # as this is the only place that needs System.Drawing. |
|---|
| 166 | bm = System.Drawing.Bitmap(fileName) |
|---|
| 167 | if bm.width <= width, return |
|---|
| 168 | height = ((width / bm.width) * bm.height) to int # scale down height proportionately |
|---|
| 169 | newBM = System.Drawing.Bitmap(width, height) |
|---|
| 170 | using g = System.Drawing.Graphics.fromImage(newBM) |
|---|
| 171 | g.compositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality |
|---|
| 172 | g.smoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality |
|---|
| 173 | g.interpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic |
|---|
| 174 | g.drawImage(bm, System.Drawing.Rectangle(0, 0, width, height)) |
|---|
| 175 | # avoid Windows file locks with a temp file, .dispose and .move: |
|---|
| 176 | tmpFileName = '-tmp-' + fileName |
|---|
| 177 | newBM.save(tmpFileName, System.Drawing.Imaging.ImageFormat.png) |
|---|
| 178 | newBM.dispose |
|---|
| 179 | bm.dispose |
|---|
| 180 | File.delete(fileName) |
|---|
| 181 | File.move(tmpFileName, fileName) |
|---|
| 182 | |
|---|