{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "We want to make a turn-based strategy game, based on a **hexagonal grid**.\n", "\n", "Wait, how do we even lay out a hexagonal grid in kivy?\n", "\n", " http://playtechs.blogspot.com/2007/04/hex-grids.html\n", " \n", " Also\n", " \n", " http://gamedev.stackexchange.com/questions/15881/hexagonal-grid-tiles-tutorials\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ignore device orientation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's lay out a grid. First we need to draw hexagons. Clone gamecamp. change the object names.\n", "Can we lay out the basic screen?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%file strategygame.kv\n", "\n", "#:include debug.kv\n", "\n", ":\n", " BoxLayout:\n", " orientation: 'horizontal'\n", " DebugLabel:\n", " text: 'Main map'\n", " size_hint: .75, 1\n", " BoxLayout:\n", " orientation: 'vertical'\n", " size_hint: .25, 1\n", " DebugLabel:\n", " text: 'status'\n", " size_hint: 1, .66\n", " DebugLabel:\n", " text: 'mini-map'\n", " size_hint: 1, .33\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%file main.py\n", "from kivy.app import App\n", "from kivy.uix.floatlayout import FloatLayout\n", "\n", "\n", "class StrategyGame(FloatLayout):\n", " pass\n", "\n", "\n", "class StrategyGameApp(App):\n", " def build(self):\n", " return StrategyGame()\n", "\n", "if __name__ == '__main__':\n", " StrategyGameApp().run()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "#!python main.py\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make a gridlayout in the game area, and give it a label so we can refer to it from our code. We are going to build a rectangular grid" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%%file strategygame.kv\n", "\n", "#:include debug.kv\n", "\n", ":\n", " main_map: _main_map\n", " BoxLayout:\n", " orientation: 'horizontal'\n", " GridLayout:\n", " id: _main_map\n", " cols: 10\n", " size_hint: .75, 1\n", " BoxLayout:\n", " orientation: 'vertical'\n", " size_hint: .25, 1\n", " DebugLabel:\n", " text: 'status'\n", " size_hint: 1, .66\n", " DebugLabel:\n", " text: 'mini-map'\n", " size_hint: 1, .33\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's hook to that from code. We will do this in `__init__` in our main StrategyGame class. Let's make a loop to add buttons to the root widget. We will use properties in the kv file to determine how many rows/cols to add. We will also need to refer to various objects in the widget hierarchy in the code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can refer to the top-level object (non-indented) as `root` in the kv file, but it we want to refer to conveniently elsewhere in the code, we need to use an id.\n", "\n", "`id: _game`\n", "\n", "and then from any widget we want to refer to it, we do a \n", "\n", "`game: _game`\n", "\n", "We'll use this to access the number of regions to layout in the grid via `map_cols` and `map_rows` from the main game widget. \n", "\n", "Fill in grids: it fills it in by default, left to right, top to bottom, and give them coordinate references. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%%file strategygame.kv\n", "\n", "#:include debug.kv\n", "\n", ":\n", " id: _game\n", " main_map: _main_map\n", " map_rows: 10\n", " map_cols: 10\n", " BoxLayout:\n", " orientation: 'horizontal'\n", " GridLayout:\n", " id: _main_map\n", " game: _game\n", " cols: root.map_cols\n", " size_hint: .75, 1\n", " BoxLayout:\n", " orientation: 'vertical'\n", " size_hint: .25, 1\n", " DebugLabel:\n", " text: 'status'\n", " size_hint: 1, .66\n", " DebugLabel:\n", " text: 'mini-map'\n", " size_hint: 1, .33\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# %load main.py\n", "from kivy.app import App\n", "from kivy import properties\n", "from kivy.uix import button\n", "from kivy.uix.floatlayout import FloatLayout\n", "\n", "\n", "class StrategyGame(FloatLayout):\n", " main_map = properties.ObjectProperty(None)\n", " map_rows = properties.NumericProperty(0)\n", " map_cols = properties.NumericProperty(0)\n", "\n", " def __init__(self, **kwargs):\n", " super(StrategyGame, self).__init__(**kwargs)\n", "\n", " number_of_regions = self.map_rows * self.map_cols\n", " for region in xrange(0, number_of_regions):\n", " row = region / self.map_cols\n", " col = region % self.map_cols\n", " self.main_map.add_widget(button.Button(text='({}, {})'.format(row, col)))\n", "\n", "\n", "class StrategyGameApp(App):\n", " def build(self):\n", " return StrategyGame()\n", "\n", "if __name__ == '__main__':\n", " StrategyGameApp().run()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We draw alternating rows with the patterns:\n", "```\n", "/ \\\n", "| |\n", "| |\n", "```\n", "and\n", "```\n", "\\ /\n", " |\n", " |\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%%file strategygame.kv\n", "\n", "#:include debug.kv\n", "\n", ":\n", " id: _game\n", " main_map: _main_map\n", " map_rows: 10\n", " map_cols: 10\n", " BoxLayout:\n", " orientation: 'horizontal'\n", " GridLayout:\n", " id: _main_map\n", " game: _game\n", " cols: root.map_cols\n", " size_hint: .75, 1\n", " BoxLayout:\n", " orientation: 'vertical'\n", " size_hint: .25, 1\n", " DebugLabel:\n", " text: 'status'\n", " size_hint: 1, .66\n", " DebugLabel:\n", " text: 'mini-map'\n", " size_hint: 1, .33\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%%file main.py\n", "from kivy.app import App\n", "from kivy import properties\n", "from kivy.uix import button\n", "from kivy.uix.floatlayout import FloatLayout\n", "\n", "\n", "class StrategyGame(FloatLayout):\n", " main_map = properties.ObjectProperty(None)\n", " map_rows = properties.NumericProperty(0)\n", " map_cols = properties.NumericProperty(0)\n", "\n", " def __init__(self, **kwargs):\n", " super(StrategyGame, self).__init__(**kwargs)\n", "\n", " number_of_regions = self.map_rows * self.map_cols\n", " for region in xrange(0, number_of_regions):\n", " row = region / self.map_cols\n", " col = region % self.map_cols\n", " self.main_map.add_widget(button.Button(text='({}, {})'.format(row, col)))\n", "\n", "\n", "class StrategyGameApp(App):\n", " def build(self):\n", " return StrategyGame()\n", "\n", "if __name__ == '__main__':\n", " StrategyGameApp().run()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wait, that doesn't draw where we think it should.'" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%%file main.py\n", "import collections\n", "\n", "from kivy.app import App\n", "from kivy import properties\n", "from kivy import graphics\n", "from kivy.uix import label\n", "from kivy.uix.floatlayout import FloatLayout\n", "\n", "\n", "MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n", "\n", "\n", "class StrategyGame(FloatLayout):\n", " main_map = properties.ObjectProperty(None)\n", " map_rows = properties.NumericProperty(0)\n", " map_cols = properties.NumericProperty(0)\n", "\n", " def __init__(self, **kwargs):\n", " super(StrategyGame, self).__init__(**kwargs)\n", "\n", " number_of_regions = self.map_rows * self.map_cols\n", " for region in xrange(0, number_of_regions):\n", " row = region / self.map_cols\n", " col = region % self.map_cols\n", " self.main_map.add_widget(HexMapCell(row=row, col=col))\n", "\n", "\n", "class HexMapCell(label.Label):\n", " def __init__(self, row=0, col=0, **kwargs):\n", " self.region_in_map = MapCoords(row, col)\n", " super(HexMapCell, self).__init__(**kwargs)\n", " self.draw_hex_edge()\n", "\n", " def draw_hex_edge(self):\n", " edge = ''\n", " if self.region_in_map.col % 2 == 0:\n", " row_mod = self.region_in_map.row % 6\n", " if row_mod == 0:\n", " edge = 'BU'\n", " with self.canvas:\n", " graphics.Color(1, 1, 1, 1)\n", " graphics.Line(points=[self.x, self.y, self.width + self.x, self.height + self.y])\n", " elif row_mod in (1, 2):\n", " edge = 'L '\n", " elif row_mod == 3:\n", " edge = 'TD'\n", " elif row_mod in (4, 5):\n", " edge = ' R'\n", " else:\n", " row_mod = self.region_in_map.row % 6\n", " if row_mod == 0:\n", " edge = 'TD'\n", " elif row_mod in (1, 2):\n", " edge = ' R'\n", " elif row_mod == 3:\n", " edge = 'BU'\n", " elif row_mod in (4, 5):\n", " edge = 'L '\n", "\n", " self.text = edge\n", "\n", "class StrategyGameApp(App):\n", " def build(self):\n", " return StrategyGame()\n", "\n", "if __name__ == '__main__':\n", " StrategyGameApp().run()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%file main.py\n", "from kivy.app import App\n", "from kivy.uix.gridlayout import GridLayout\n", "from kivy.lang import Builder\n", "from kivy.properties import NumericProperty\n", "\n", "class HelloWorld(GridLayout):\n", " cols = NumericProperty(4)\n", " def __init__(self, **kw):\n", " super(HelloWorld, self).__init__(**kw)\n", " self.add_widget(self.BU())\n", " self.add_widget(self.TD())\n", " self.add_widget(self.BU())\n", " self.add_widget(self.TD())\n", "\n", " def BU(self):\n", " return Builder.load_string('''\n", "Label:\n", " canvas:\n", " Color:\n", " rgba: (1,1,1,1)\n", " Line:\n", " points: (self.x, self.y, self.right, self.top)\n", " width: 2''')\n", "\n", " def TD(self):\n", " return Builder.load_string('''\n", "Label:\n", " canvas:\n", " Color:\n", " rgba: (1,1,1,1)\n", " Line:\n", " points: (self.x, self.top, self.right, self.y)\n", " width: 2''')\n", "\n", " def L(self):\n", " return Builder.load_string('''\n", "Label:\n", " canvas:\n", " Color:\n", " rgba: (1,1,1,1)\n", " Line:\n", " points: (self.x, self.y, self.x, self.top)\n", " width: 2''')\n", "\n", " def R(self):\n", " return Builder.load_string('''\n", "Label:\n", " canvas:\n", " Color:\n", " rgba: (1,1,1,1)\n", " Line:\n", " points: (self.right, self.y, self.right, self.top)\n", " width: 2''')\n", "\n", "\n", "class HelloWorldApp(App):\n", " def build(self):\n", " return HelloWorld()\n", "\n", "if __name__ == '__main__':\n", " HelloWorldApp().run()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# %load strategygame.kv\n", "\n", "#:include debug.kv\n", "\n", ":\n", " id: _game\n", " main_map: _main_map\n", " map_rows: 10\n", " map_cols: 10\n", " BoxLayout:\n", " orientation: 'horizontal'\n", " GridLayout:\n", " id: _main_map\n", " game: _game\n", " cols: root.map_cols\n", " size_hint: .75, 1\n", " BoxLayout:\n", " orientation: 'vertical'\n", " size_hint: .25, 1\n", " DebugLabel:\n", " text: 'status'\n", " size_hint: 1, .66\n", " DebugLabel:\n", " text: 'mini-map'\n", " size_hint: 1, .33\n" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Trying to draw the hexmap\n", "\n", "We tried adding the ability to style our widgets for to draw the Hexmap using lines like this\n", "\n", " BU: /\n", " TD: \\\n", " L: |\n", " R: |\n", "We get hexs now, but we need to figure out how to do the aspect ratio properly now...for a spiky top, we need each grid widget to have aspec ratio $$\\sqrt{3}\\cdot height = width$$" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%%file strategygame.kv\n", "\n", "#:include debug.kv\n", "\n", ":\n", " id: _game\n", " main_map: _main_map\n", " map_rows: 10\n", " map_cols: 10\n", " BoxLayout:\n", " orientation: 'horizontal'\n", " GridLayout:\n", " id: _main_map\n", " game: _game\n", " cols: root.map_cols\n", " size_hint: .75, 1\n", " BoxLayout:\n", " orientation: 'vertical'\n", " size_hint: .25, 1\n", " DebugLabel:\n", " text: 'status'\n", " size_hint: 1, .66\n", " DebugLabel:\n", " text: 'mini-map'\n", " size_hint: 1, .33\n", "\n", ":\n", " canvas:\n", " Color:\n", " rgba: (1,1,1,1)\n", " Line:\n", " points: (self.x, self.y, self.right, self.top)\n", " width: 2\n", ":\n", " canvas:\n", " Color:\n", " rgba: (1,1,1,1)\n", " Line:\n", " points: (self.x, self.top, self.right, self.y)\n", " width: 2\n", ":\n", " canvas:\n", " Color:\n", " rgba: (1,1,1,1)\n", " Line:\n", " points: (self.x, self.y, self.x, self.top)\n", " width: 2\n", ":\n", " canvas:\n", " Color:\n", " rgba: (1,1,1,1)\n", " Line:\n", " points: (self.right, self.y, self.right, self.top)\n", " width: 2\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%file main.py\n", "import collections\n", "\n", "from kivy.app import App\n", "from kivy import properties\n", "from kivy import graphics\n", "from kivy.uix import label\n", "from kivy.uix.floatlayout import FloatLayout\n", "import math\n", "\n", "MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n", "\n", "\n", "class StrategyGame(FloatLayout):\n", " main_map = properties.ObjectProperty(None)\n", " map_rows = properties.NumericProperty(0)\n", " map_cols = properties.NumericProperty(0)\n", "\n", " def __init__(self, **kwargs):\n", " super(StrategyGame, self).__init__(**kwargs)\n", "\n", " number_of_regions = self.map_rows * self.map_cols\n", " for region in xrange(0, number_of_regions):\n", " row = region / self.map_cols\n", " col = region % self.map_cols\n", " self.main_map.add_widget(self.pick_hex_cell(row=row, col=col))\n", "\n", "\n", " def pick_hex_cell(self, row, col):\n", " row_mod = row % 6\n", " if col % 2 == 0:\n", " if row_mod == 0:\n", " return BU()\n", " elif row_mod in (1, 2):\n", " return L()\n", " elif row_mod == 3:\n", " return TD()\n", " elif row_mod in (4, 5):\n", " return R()\n", " else:\n", " if row_mod == 0:\n", " return TD()\n", " elif row_mod in (1, 2):\n", " return R()\n", " elif row_mod == 3:\n", " return BU()\n", " elif row_mod in (4, 5):\n", " return L()\n", "\n", "\n", "class HexMapCell(label.Label):\n", " def __init__(self, row=0, col=0, **kwargs):\n", " self.region_in_map = MapCoords(row, col)\n", " super(HexMapCell, self).__init__(**kwargs)\n", "\n", "class BU(HexMapCell):\n", " pass\n", "class TD(HexMapCell):\n", " pass\n", "class L(HexMapCell):\n", " pass\n", "class R(HexMapCell):\n", " pass\n", "\n", "\n", "class StrategyGameApp(App):\n", " def build(self):\n", " return StrategyGame()\n", "\n", "if __name__ == '__main__':\n", " StrategyGameApp().run()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.11" } }, "nbformat": 4, "nbformat_minor": 0 }