Skip to content

Commit 1c77cb0

Browse files
committedMar 12, 2015
all the things
0 parents  commit 1c77cb0

12 files changed

+376
-0
lines changed
 

‎.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
dist
3+
node_modules
4+
.DS_Store

‎README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Hex Demo
2+
========
3+
4+
Demo of a resizeable hex grid. Inspired by this article:
5+
http://www.redblobgames.com/grids/hexagons/
6+
7+
To view - hexdemo

‎gulpfile.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Tasks
2+
// ------
3+
// TODO - make gulp watch only rebuild changed items
4+
// TODO - ensure gulp watch doesn't exit when an error occurs
5+
// TODO - build with specific development or production options (e.g. uglify and compress javascripts, css, images in production)
6+
7+
var source = require('vinyl-source-stream');
8+
var browserify = require('browserify');
9+
var reactify = require('reactify');
10+
var gulp = require('gulp');
11+
var del = require('del');
12+
var less = require('gulp-less-sourcemap');
13+
var path = require('path');
14+
var chmod = require('gulp-chmod');
15+
16+
gulp.task('default', ['build']);
17+
gulp.task('build', ['images', 'fonts', 'js', 'html', 'css']);
18+
19+
// clean the dist directory
20+
gulp.task('clean', function(cb) {
21+
del(['./dist/**/*'], cb);
22+
});
23+
24+
// copy over html files
25+
gulp.task('html', ['clean'], function() {
26+
return gulp.src('./src/html/index.html')
27+
.pipe(gulp.dest('./dist/'));
28+
});
29+
30+
// use browserify to package js, transform react jsx files, and bundle all together
31+
gulp.task('js', ['clean'], function() {
32+
var b = browserify({ debug:true });
33+
var stream = b
34+
.add('./src/js/index.js')
35+
.transform({}, reactify)
36+
.bundle();
37+
return stream
38+
.pipe(source('app.js'))
39+
.pipe(gulp.dest('./dist/js'));
40+
});
41+
42+
// compile css using less processor
43+
gulp.task('css', ['clean'], function() {
44+
gulp.src('./src/less/main.less')
45+
.pipe(less())
46+
.pipe(gulp.dest('./dist/css'));
47+
});
48+
49+
gulp.task('images', ['clean'], function() {
50+
gulp.src(['./src/assets/images/**/*.png', './src/assets/images/**/*.jpg'])
51+
.pipe(chmod(644))
52+
.pipe(gulp.dest('./dist/assets/images'));
53+
});
54+
55+
gulp.task('fonts', ['clean'], function() {
56+
gulp.src(['./src/less/fonts/**/*', '!./src/less/fonts/**/*.txt'])
57+
.pipe(chmod(644))
58+
.pipe(gulp.dest('./dist/css/fonts'));
59+
});
60+
61+
// watch
62+
gulp.task('watch', function() {
63+
gulp.watch('./src/js/**', ['build']);
64+
gulp.watch('./src/html/**', ['build']);
65+
gulp.watch('./src/less/**/*.less', ['build']);
66+
gulp.watch('./src/less/fonts/**', ['fonts']);
67+
gulp.watch('./src/assets/images/**', ['images']);
68+
});

‎package.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "hex-demo",
3+
"version": "0.0.0",
4+
"description": "A space game",
5+
"main": "dist/index.html",
6+
"scripts": {
7+
"build": "gulp"
8+
},
9+
"author": "Daniel Richards",
10+
"license": "none",
11+
"devDependencies": {
12+
"gulp": "^3.8.8",
13+
"browserify": "^6.0.3",
14+
"vinyl-source-stream": "^1.0.0",
15+
"gulp-streamify": "0.0.5",
16+
"del": "^0.1.3",
17+
"reactify": "^0.14.0",
18+
"gulp-less-sourcemap": "^1.3.3",
19+
"gulp-chmod": "^1.1.1",
20+
"seedrandom": "^2.3.10"
21+
},
22+
"dependencies": {
23+
"react": "^0.11.2",
24+
"react-async": "^1.0.2",
25+
"react-router-component": "^0.22.0",
26+
"underscore": "^1.7.0",
27+
"rsvp": "^3.0.14",
28+
"superagent": "^0.20.0",
29+
"react-art": "^0.11.0"
30+
}
31+
}

‎src/html/index.html

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head lang="en">
4+
<meta charset="UTF-8">
5+
<title>Hex Demo</title>
6+
<link href="./css/main.css" rel="stylesheet" type="text/css" />
7+
</head>
8+
<body>
9+
10+
<script src="./js/app.js"></script>
11+
</body>
12+
</html>

‎src/js/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var React = require('react');
2+
var App = require('./jsx/app.jsx');
3+
4+
React.renderComponent(App(), document.body);

‎src/js/jsx/app.jsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
var React = require('react');
2+
var GameBoard = require('./game-board.jsx');
3+
4+
module.exports = React.createClass({
5+
render: function() {
6+
return (
7+
<GameBoard />
8+
);
9+
}
10+
});

‎src/js/jsx/game-board.jsx

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
var React = require('react');
2+
var Art = require('react-art');
3+
var HexGrid = require('./hex-grid.jsx');
4+
var Display = require('../utilities/display.js');
5+
6+
var Surface = Art.Surface;
7+
8+
module.exports = React.createClass({
9+
getInitialState: function() {
10+
return {
11+
displayDimensions: Display.getDimensions()
12+
}
13+
},
14+
setDisplayDimensions: function(dimensions) {
15+
this.setState({
16+
displayDimensions: Display.getDimensions()
17+
});
18+
},
19+
componentWillMount: function() {
20+
Display.subscribeResize(this.setDisplayDimensions);
21+
},
22+
componentWillUnmount: function() {
23+
Display.unsubscribeResize(this.setDisplayDimensions);
24+
},
25+
render: function() {
26+
var width = this.state.displayDimensions.width;
27+
var height = this.state.displayDimensions.height;
28+
29+
return (
30+
<Surface width={ width } height={ height }>
31+
<HexGrid width={ width } height={ height } hexCountHorizontal='19' hexCountVertical='13' />
32+
</Surface>
33+
);
34+
}
35+
})

‎src/js/jsx/hex-grid.jsx

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
var React = require('react');
2+
var Art = require('react-art');
3+
var _ = require('underscore');
4+
var HexTile = require('./hex-tile.jsx');
5+
var Group = Art.Group;
6+
7+
var SIZE_TO_PACKED_WIDTH = 1.7320508075688772;
8+
var SIZE_TO_PACKED_HEIGHT = 1.5;
9+
10+
function getOptimalSize(widthPixels, heightPixels, countHorizontal, countVertical) {
11+
// add extra amounts for offset rows and bottom points of hexes on last row
12+
var packedhexWidth = widthPixels / (parseInt(countHorizontal, 10) + 0.5);
13+
var adjustedGridHeight = (heightPixels * SIZE_TO_PACKED_HEIGHT) / (parseInt(countVertical, 10) + 0.5);
14+
var packedHexHeight = adjustedGridHeight / SIZE_TO_PACKED_HEIGHT;
15+
16+
var size = null;
17+
18+
if ((packedhexWidth / SIZE_TO_PACKED_WIDTH) < (packedHexHeight / SIZE_TO_PACKED_HEIGHT)) {
19+
size = packedhexWidth / SIZE_TO_PACKED_WIDTH;
20+
}
21+
else {
22+
size = packedHexHeight / SIZE_TO_PACKED_HEIGHT;
23+
}
24+
25+
return size.toFixed(8);
26+
}
27+
28+
function calculatePixelCoordinates(baseVector, hexSize, axialXCoord, axialYCoord) {
29+
return {
30+
x: baseVector.x + (hexSize * Math.sqrt(3) * (axialXCoord + axialYCoord / 2)),
31+
y: baseVector.y + (hexSize * 3 / 2 * axialYCoord)
32+
};
33+
}
34+
35+
function setupHexPositionsRadial(widthPixels, heightPixels, countHorizontal, countVertical) {
36+
var size = getOptimalSize(
37+
widthPixels,
38+
heightPixels,
39+
countHorizontal,
40+
countVertical
41+
);
42+
43+
var centreX = Math.floor(widthPixels / 2);
44+
var centreY = Math.floor(heightPixels / 2);
45+
var gridRadiusVertical = (countVertical - 1) / 2;
46+
var centreVector = {
47+
x: Math.floor(widthPixels / 2),
48+
y: Math.floor(heightPixels / 2)
49+
};
50+
var hexSize = parseFloat(size, 10);
51+
var widthOffset = (countHorizontal - countVertical) / 2;
52+
53+
var rows = [];
54+
_.times(countVertical, function(indexVertical) {
55+
var axialYCoord = indexVertical - gridRadiusVertical;
56+
var distanceFromCentreVertical = Math.abs(axialYCoord);
57+
var adjustedCountHorizontal = countHorizontal - distanceFromCentreVertical;
58+
59+
var row = [];
60+
61+
_.times(adjustedCountHorizontal, function(indexHorizontal) {
62+
var axialXCoord = indexHorizontal - Math.min(indexVertical, gridRadiusVertical) - widthOffset;
63+
var cubeXCoord = 0;
64+
var cubeYCoord = 0;
65+
66+
row.push({
67+
axialCoordinates: {
68+
x: axialXCoord,
69+
y: axialYCoord
70+
},
71+
cubeCoordinates: {
72+
x: axialXCoord,
73+
y: (-axialXCoord) - axialYCoord,
74+
z: axialYCoord
75+
},
76+
size: hexSize - 0.4,
77+
pixelCoordinates: calculatePixelCoordinates(centreVector, hexSize, axialXCoord, axialYCoord)
78+
});
79+
});
80+
81+
rows.push(row);
82+
});
83+
84+
return rows;
85+
}
86+
87+
module.exports = React.createClass({
88+
render: function() {
89+
var widthPixels = this.props.width;
90+
var heightPixels = this.props.height;
91+
var hexCountHorizontal = this.props.hexCountHorizontal;
92+
var hexCountVertical = this.props.hexCountVertical;
93+
94+
var hexPositions = setupHexPositionsRadial(
95+
widthPixels,
96+
heightPixels,
97+
hexCountHorizontal,
98+
hexCountVertical
99+
);
100+
101+
var hexGrid = _.map(hexPositions, function(hexRow) {
102+
var rowElements = _.map(hexRow, function(hexData) {
103+
return (
104+
<HexTile size={hexData.size} centre={hexData.pixelCoordinates}></HexTile>
105+
);
106+
});
107+
108+
return (
109+
<Group>
110+
{ rowElements }
111+
</Group>
112+
);
113+
});
114+
115+
return (
116+
<Group>
117+
{ hexGrid }
118+
</Group>
119+
);
120+
}
121+
});

‎src/js/jsx/hex-tile.jsx

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
var React = require('react');
2+
var Art = require('react-art');
3+
4+
var Shape = Art.Shape;
5+
var Path = Art.Path;
6+
7+
function makeHexPath(size, centre) {
8+
var path = new Path();
9+
var point = 0;
10+
var angle = null;
11+
var x = null;
12+
var y = null;
13+
14+
while (point < 6) {
15+
angle = 2 * Math.PI / 6 * (point + 0.5);
16+
x = centre.x + size * Math.cos(angle);
17+
y = centre.y + size * Math.sin(angle);
18+
19+
if (point === 0) {
20+
path.moveTo(x, y);
21+
}
22+
else {
23+
path.lineTo(x, y);
24+
}
25+
26+
point = point + 1;
27+
}
28+
29+
return path;
30+
}
31+
32+
module.exports = React.createClass({
33+
getInitialState: function() {
34+
return {
35+
isSelected: false
36+
};
37+
},
38+
onClick: function() {
39+
this.setState({
40+
isSelected: !this.state.isSelected
41+
});
42+
},
43+
render: function() {
44+
var color = this.state.isSelected ? '#888' : '#111';
45+
46+
// TODO - this could be optimised, don't need to calculate coords for every hex, just one and then offset.
47+
var path = makeHexPath(this.props.size, this.props.centre);
48+
49+
return (
50+
<Shape d={path} fill={color} opacity='0.5' onClick={this.onClick}></Shape>
51+
);
52+
}
53+
});

‎src/js/utilities/display.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
var Events = require('events');
2+
var _ = require('underscore');
3+
4+
var eventEmitter = new Events.EventEmitter();
5+
6+
function onResize() {
7+
eventEmitter.emit('resize', {
8+
width: window.innerWidth,
9+
height: window.innerHeight
10+
});
11+
}
12+
window.addEventListener('resize', _.debounce(onResize, 200));
13+
14+
module.exports = {
15+
subscribeResize: function(onResize) {
16+
eventEmitter.addListener('resize', onResize);
17+
},
18+
unsubscribeResize: function(onResize) {
19+
eventEmitter.removeListener('resize', onResize);
20+
},
21+
getDimensions: function() {
22+
return {
23+
width: window.innerWidth,
24+
height: window.innerHeight
25+
}
26+
}
27+
};

‎src/less/main.less

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
body {
2+
padding: 0;
3+
margin: 0;
4+
}

0 commit comments

Comments
 (0)
Please sign in to comment.