?? full tutorial.htm
字號(hào):
mouse Deals with a mouse button press, setup_player Sets up an individual player (creates bases, etc.). update Update the game board</pre><p>The <i>initialise, setup_player</i> and <i>draw</i> functions arecalled at the very start of the game (we only need to draw the wholeboard once). The <i>key, mouse</i> and <i>update</i> functions arecalled as the game is played to respond to player commands and updatethe board to reflect the results of those commands.<p>The <i>Board</i> functions are largely self-explanatory although thereare a few things that are not obvious. For example, given thecoordinates of a mouse click in the game window just how do you findout exactly which side of which hexagonal cell it points to? This, andsome other tricky bits, is explained in the sections that follow.<h3>Grid Layout</h3><p>The board is a two-dimensional array of hexagonal cells. The boardcells are numbered as shown below:<P><IMG SRC="Full Tutorial_files/image007.gif" ><h3>Identifying a Cell</h3><p>To identify the tile indices from the window coordinates we mentallychop up the board into a grid so that we will know exactly what wewill find at the target point. In the same way that alternate columnsare offset by half a cell on the board we also offset our alternateimaginary grid columns as shown below (in red).<P><IMG SRC="Full Tutorial_files/image008.gif" ><p>If the x coordinate specifies an odd numbered grid column, all we needto do is shift the y coordinate up half a cell before computing cellindices. We can then treat each cell the same way by first assumingthat the x and y coordinates pick the cell that dominates the area andthen adjusting the indices if the actual point is outside thatcell. Here's what the code looks like to identify which square on ourimaginary grid we are dealing with (ignoring checking for coordinatesthat are off the board). Note that we are starting with Windowscoordinates in x and y and we want to end up with two array indices: iand j;<pre> int i (x / (cell_size - (cell_size / 4))); int j (y / cell_size); if ((i & 1) != 0) { j = (y - half_cell_size) / cell_size; }</pre><p>The triangular portion of the hexagon that goes over our imaginarygrid line is 1/4 the size of the cell (so i is actually the x coordinatedivided by 3/4 of the cell size. Next we need to start adjusting forthose tricky triangular portions:<P><IMG SRC="Full Tutorial_files/image009.gif" ><p>If the coordinates hit one of the 2 corner areas then we will need todecrement the x index to identify the correct cell. If the top corneris selected and the column number is even then we need to decrementthe y index. If the bottom corner is selected and the column number isodd then we need to increment the y index. The technique to deal withthe triangular area is to split the hexagon through the middle thenperform a boundary check based on the gradient of the sides. Thegradient of a hexagon side is actually a constant equal to thefollowing:<pre> (1/4 cell size) / (1/2 cell size)</pre><p>Now that we have made an initial stab at the cell indices we canretrieve the coordinates of the cell that dominates the square on ourimaginary grid (ox and oy). This allows us to adjust for the realvalues we want as described above. The code is as follows:<pre> if (oy < half_cell_size) { boundary = quarter_cell_size - (int) (gradient * oy); if (ox < boundary) { if ((i & 1) == 0) j--; i--; } } else { boundary = (int) (gradient * (oy - half_cell_size)); if (ox < boundary) { if ((i & 1) != 0) j++; i--; } }</pre><p>We now have the indices of the cell that corresponds to a particularWindows coordinate and this vital function is packaged in the privateutility function <i>get_cell</i>.<h3>Drawing Movement Vectors</h3><p>Movement vectors are drawn from the centre of a cell to the middle ofa side using the player's colour (or half way if the cell isempty). If troops are present in the cell then a black segment isdrawn to the edge of the troop circle. As we know the length of thevector (or can calculate it) and the angle from the horizontal, wejust need to convert from the polar coordinates into their Cartesianequivalents. However, as shown in the diagram below, the x and yco-ordinates are quite easy to compute directly as constants and areprovided by table lookup (the x offset for a vector that isn'tstraight up or down is 3/8 the cell size with a y offset of 1/2 the cellsize).<P><IMG SRC="Full Tutorial_files/image010.gif" ><h3>Identifying a Side</h3><p>If we want to identify which hexagon side is near a mouse click wehave the opposite problem to the previous example. We have Cartesiancoordinates and we want to convert them to polar ones so that we knowthe angle. Once we have that, the side can be identified by simplycalculating angle/60. The formula for calculating the angle is fundamentally:<pre> angle = tan-1 (y/x)</pre><p>However, this is not the whole story as there are boundary conditionsto worry about (for example when x is zero y must not be divided byit). Fortunately, all of the problems are hidden away in the<i>atan2</i> library function so the code we need to write is quitesimple:<pre> double radians (atan2(y, x)); double angle (radians * (180.0 / pi)); if (angle < 0) angle += 360; return static_cast<int> (angle / 60.0);</pre><h3>Using The Socket Class</h3><p>This project uses a slightly updated version of a Socket class that Iposted on CodeGuru late 2003. To use the socket class you will need toinclude socket.h in your code and incorporate socket.cpp as amodule. You will also need to include the socket library ws2_32.lib inyour link parameters. Now, what are all those things in the socketinclude file?<pre>#include "exception.h"</pre><p>C++ lets you handle errors with exceptions. Some people like them,some people don't. I do, so if I detect an error I throw an exception(the exception class that I use is included in the project). Irecommend you use exceptions as they are a great help indebugging. Always put any code using the socket class inside a tryblock (note the caveat about streams below, though) and catch andreport any errors. For example:<pre> try { my_routine_that_uses_sockets(); } catch (Exception & e) { MessageBox(0, e.get_error().c_str(), "Ooops!", MB_SETFOREGROUND); }</pre><p>Now there is one thing to watch out for. If there is an error whileyou are streaming (using the << or >> operators) then the standardlibrary will catch the thrown exception and quietly set the streamstate to <i>bad</i> or <i>fail</i> (depending on the error). If youstream your data you must use the standard library error testfunctions <i>fail()</i> and <i>bad()</i> and not rely on an exceptionbeing caught.<pre> void close (); void connect (const char * const host, const int port); void listen ();</pre><p>These functions have been covered in the tutorial above. There'sreally not much more to say apart from the fact that if you give aport number of zero when creating a socket it will automaticallyallocate a free port number (you can see this being used in theclient-side code). If you want to find out what number has beenallocated use the following call:<pre> int get_number ();</pre><p>This is used by the client program to get its socket number so that itcan tell the server what it is.<pre> int bytes_read (bool reset_count = false); int bytes_sent (bool reset_count = false);</pre><p>You can read (and reset) the number of bytes sent and received over asocket. I used this when I wanted to display a progress bar whilesending files over a socket in another project.<pre> void getline (std::string & s);</pre><p>The Microsoft string getline function doesn't work with sockets (itcan block forever). Consequently, this is a replacement (which alsodoesn't include the eol character in the result ... just a personalpreference).<pre> void read_binary (void * buffer, int buffer_size); void write_binary (void * buffer, int buffer_size);</pre><p>Streaming is great because it is just so easy to read (and modern PCsare so fast that it is rare that you notice the overhead). However, ifyou are sending lots of data very often (especially floating pointnumbers) it can be very CPU intensive (i.e. slow) convertingeverything to text and back. So, if you need performance, use thesebinary functions. Define a structure containing the elements you wantto send and call <i>write_binary</i> at the sender and<i>read_binary</i> at the receiver. You won't get data transferbetween networked PCs much faster than that.<pre> void set_trace (const char * filename);</pre><p>If things are not working as expected and you can't figure out what'sgoing wrong then turn on tracing. Pass a file name to this functionand everything streamed over the sockets will be logged to that file(another good reason for streaming). Look at it to see exactly whatwas sent and what was received by each program (saves bags of debugtime).<pre> void set_connection (void * handle); void * get_connection ();</pre><p>These functions let you transfer details from a socket established bya listen to a new one (so that the original socket can go back tolistening). It was demonstrated in the <i>game_handler</i> threaddescription.<pre>private: Socket (const Socket & Socket); // No copying allowed</pre><p>No copying of Sockets is allowed because I haven't written a copyconstructor ... yet (and I'm not sure I want to). Why? The Socketclass contains a buffer where it builds up text to send. Taking a copy(passing the socket as a parameter to a function) and adding to thebuffer (inside the function) and then reverting to the original buffer(returning from the function call) is just too error prone. I justdon't want to think about it. Does this mean you can't pass a socketas a function parameter? No, it doesn't, just pass it by referencerather than by copy. For example:<pre>void my_function(Socket & socket);</pre><h3>Things To Do</h3><p>The project as it stands is the result of a few weekend'stinkering. Consequently, there are loads of features in the originalgame that aren't present in this version. If you would like to extendthe game please do.<h3>Bugs</h3><p>I've tried it out on Windows XP, 2000 and NT4. It should work onWindows 95 and later but I haven't been able to test that. There areprobably plenty of bugs in the code so feel free to fix any that youcome across :-). I'd also appreciate an e-mail letting me know of anythat you find.<p>One problem I have seen is that sometimes when I compile it on my XPmachine it seems to introduce a dependency on <i>gdiplus.dll</i>. I don'tknow why (yet), but if the programs bomb out on start-up it's a goodchance they are looking for that DLL.<h3>Licence</h3><p>WinBattle is free software; you can redistribute it and/or modify itunder the terms of the GNU General Public License (GPL) version 2 aspublished by the Free Software Foundation. You will find a copy of theGPL and the original XBattle license in the project <i>notes</i> folder.<h3>Contact</h3><p>After having two e-mail accounts rendered totally unusable by spam Idon't publish my e-mail address on web pages or innewsgroups. However, you are welcome to e-mail me should you want. Myaddress can be found in the file <i>contact.txt</i> in the notesfolder.
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -