Oxyd

Members
  • Content count

    833
  • Joined

  • Last visited

Community Reputation

1162 Excellent

About Oxyd

  • Rank
    Advanced Member
  1. void Application::removeEnemy(EnemyIDOrWhatever e) { enemies.erase(e); if (enemies.empty()) callback(); } Just like that. You'd call application.removeEnemy(e) instead of application.enemies.erase(e) everywhere you want to remove an enemy. You can make the Application::enemies vector private to make sure you don't accidentally call .erase directly on the vector.
  2. There's no reason why you couldn't have both. If you have your grid of stuff, then assuming all of your entities (like nails) are placed in the centre of each tile, you can easily calculate the actual coordinates of the entity: If your nail is on tile (m, n), then its coordinates are x = tile_width * m + tile_width / 2, y = tile_height * n + tile_height / 2 – this is the centre of the nail's bounding rectangle. Now if you also have the coordinates of the player, you can easily do your collision checking. In fact, your player can only be on at most two tiles at once (when it's at the border between two tiles), which simplifies your collision checking significantly – you only check for collisions on the tiles the player is overlapping with. Since you want to be able to move your player in a continuous manner, you'll probably want to represent its location either directly in coordinates (and then calculating the tile it's on from those coordinates), or as the tile + offset within the tile. This of course applies to anything else that's allowed to move continuously in your world. In other words, have a grid representation that you'll use for interacting with the world with each tile having a list of entities that are located on it (so that you can do something to those entities when you click the tile or when the tile falls into the void or whatever), and also have continuous Euclidean coordinates for collisions and rendering things on screen. With this approach, you'll have to do some thinking about entities that can stand inbetween tiles – i.e. chiefly the player. Concretely, you'll need to consider the question of which tile the entity is on – is it on the original tile until it moves completely onto the other one? Does it switch exactly halfway through? Is it on both tiles at the same time for a brief period of time? This depends on the results you want to achieve – if the player is halfway between tiles and one of the tiles dispappears, does the player die? It also affects how you process things – if let's say you drop a bomb on a tile that damages all entities within a 5-tile radius, you want to process each entity only once – if an entity is simultaneously registered on two tiles, you'll have to set some flag to make sure you don't process it twice. If it can only ever be registered on one tile, then you don't have to worry.
  3. You might find chess boxing interesting.
  4. Paradox Ideas

    Yes, clearly it does. There's no paradox here. (Talking about naive set theory here. Zermelo and Fraenkel definitely would have something else to say here.) Whoever is the author of that line probably was after something slightly different: “If S is a set of all sets that don't contain themselves, does S contain itself?” Of course, that doesn't roll of the tongue so easily, so maybe not a great choice for a level title. :) There's also a pretty popular version of that same paradox with a barber: “If a village barber shaves all men in his village who don't shave themselves, does the barber shave himself too?” It's less mathy, but still doesn't roll of the tongue too nicely.
  5. C++ constexpr challenge

    I think the general point here is that the compiler has to essentially contain an interpreter of C++ in order to evaluate constant expressions at compile-time. And, in general, the compiler could be running on a different architecture than it's generating code for. So, if you were able to examine the bit representation of objects – such as pointers –, the compiler would not only have to be able to interpret stuff, it would also have to interpret stuff as though it ran on the target architecture, as opposed to interpreting it on the architecture it's actually being run on. You can already see this in compile-time evaluation of floating-point expressions: It is permitted for a constexpr evaluation of a floating-point expression to give a different result than a run-time evaluation of the same expression would give. This is to ease the burden on compilers by not requiring them to exactly replicate the behaviour of the target CPU and instead allow them to use the host CPU for evaluating floating-point expressions. So, it seems to me that the choice was between 1) we don't allow examining bit representations at compile-time at all; 2) we allow the results of examination of bit representations to differ between run-time and compile-time; or 3) we burden the compiler with exactly emulating the target architecture. You're free to argue that you would've preferred options number 2 or 3, but in the end option number 1 is what we have to deal with. This is all a speculation on my part, though. If someone is willing to dig up the exact rationale behind this, that would be nice.
  6. C++ constexpr challenge

    Not possible. In constant expressions, you don't have access to the bit representation of an arbitrary value. More specifically, you can't do anything that would have the effect of a reinterpret_cast. Either satisfy yourself with just integers (why would you want to swap the bytes of anything else, anyway? If you swap the bytes of, say, std::vector<std::string>, you'll get a very unhappy program), or pass in an array of bytes and get an array of bytes in return.
  7.   The correct, perfect forwarding solution would be this: template<typename... Args> BlendValue(Args&&... args) : v(std::forward<Args>(args)...) { } I still can't give a good understandable explanation as to why you have to use Args&& myself, so I'll just point you to a blog if you are interested:   http://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c/   (or just search for "perfect forwarding" and you should find lots of examples).   Simply put, because T&& is special (in contexts where T is deduced), but T alone is not.   Consider, for example, this: template <typename T> void foo(T); … int i = 4; foo(i); foo(5);In both calls, T is deduced to be int – not int&, not int&&, just int. Therefore, the information about whether the function is called with an lvalue or an rvalue is lost there. If, instead, you change foo to be template <typename T> void foo(T&&);then T will be deduced to either int& (in the foo(i) call) or int&& (in the foo(5) call). Thus you retain the information about whether the function is called with an lvalue or an rvalue. The article that you linked covers the details of how that works, so I won't repeat them here. For parameter packs it's the same, except there's multiple of them.
  8. Usually, compilers allow you to override that assignment-in-if warning by placing an extra pair of parens around the assignment expression. So: #define if_once(_name) static boolean _name = true; if(_name && !((_name = false))) I would avoid the solutions based on the “++tag” thing. ++'ing a bool has been deprecated since forever and it will be removed completely in C++17.
  9. Sharing ideas

    It's big companies that do that. I work for a small company (I work on Factorio actually) and I'm not bound by any NDA whatsoever. The point of not sharing everything with the community is that rumour has a way of spreading and starting a life of its own. You say “Oh, I'm working on X” somewhere public and a week later, there's a Reddit post saying “the devs have confirmed that X will be in the next release”. Then the feature gets removed and everyone loses their mind saying “But you promised us that X will be there!” That is undesirable. Although I suppose this is only a problem when you're working on a game that already has a community – there's less danger of rumour starting a life of its own when you have an audience of three people.
  10. Seeing how you need it public in order to make the player move, I'd say you have a pretty strong case of “really need to not make it private” here. Also consider that your public move() function doesn't have to contain the movement logic: It can simply set a flag inside the player to indicate that it's currently moving in a given direction. The movement itself can then happen in the Player's update function.
  11. Assuming Dummy<Type>::type will always be Type, you can just do   template <typename Type> void dummyFunction(Type type) { }
  12. Linear transforms plus translations gives you what's known as affine transformations. To represent an affine transformation in 2D, you actually need just a 2x3 matrix – the last column being the translation vector. If you use full 3x3 matrices, you also get the ability to represent perspective projections, in addition to linear transforms and translations.   For affine transformations, it really is about just tacking the 1 at the end of your vector. In fact, if you multiply a 2x3 matrix with a 3x1 vector, you get a 2x1 vector out – losing the extra 1 that got tacked at the end. If you tacked any other number at the end, your translation vector would be multiplied by that number before being applied to your vector – which in most cases is undesirable.   With full 3x3 matrices, this becomes a bit more interesting, since not only does 3x3 matrix multiplied with 3x1 vector give you a 3x1 vector out again, it can also change the last coordinate, so it's not 1 any more. If you now have a general vector in homogeneous coordinates (x, y, W) and want to get back to 2D, what you do is you divide the first coordinates with the last, so you get (x/W, y/W) – this division moves the point closer to the origin the larger W is (and vice versa), and it's what gives you the perspective thing of making things further from the camera appear smaller and closer together. Usually, of course, people mostly care about this when doing 3D rather than 2D, but the same principle applies in 3D as well.   If you just leave the last row of your 3x3 matrix to be (0, 0, 1), this matrix won't do anything to the last coordinate of your vector, which means you'll get no projective stuff and you'll be left with just an affine transformation. You will however still get a 3x1 vector out of multiplication with such a matrix, which means you can then multiply it with another 3x3 matrix without having to tack the 1 at the end of the vector again. Also, if you have two 3x3 matrices, you can simply multiply them together to get another 3x3 matrix that represents the transformation done by both matrices – if you used 2x3 affine matrices, you wouldn't be able to multiply them together because of their incompatible dimensions. For these reasons, people usually stick to full 3x3 matrices even when they're not interested in projective transformations.
  13. To got into a bit more detail, a linear transform (i.e. a 2x2 matrix in 2D) cannot move the origin anywhere. Consider any 2x2 matrix M = [a, b; c, d] – no matter what a, b, c, d are, the point (0, 0) always maps to itself under M: [a, b; c, d] * (0, 0) = (0 * a + 0 * b, 0 * c + 0 * d) = (0, 0).   This means that while you can do scaling and rotation about the origin with a 2x2 matrix, you cannot do translations: If you move every point in the space in some direction, you necessarily move the origin as well – but as I just showed, a 2x2 matrix cannot do that.   So the trick is to go one dimension higher and then project the result back to the original dimension. In homogeneous coordinates, your origin maps to the vector (0, 0, 1) – since it's not all zeroes, matrix multiplication now can do something nontrivial with it.
  14. Fast Approximation to memcpy()

    Did somebody say templates? #include <cstddef> #include <iostream> template <std::size_t I, std::size_t N> struct memcpy_helper { static void do_(char* dst, char* src) { dst[I] = src[I]; memcpy_helper<I + 1, N>::do_(dst, src); } }; template <std::size_t N> struct memcpy_helper<N, N> { static void do_(char*, char*) { } }; template <std::size_t N, typename T> void memcpy(T* dst, T* src) { memcpy_helper<0, N * sizeof(T)>::do_(reinterpret_cast<char*>(dst), reinterpret_cast<char*>(src)); } int main() { int src[]{1, 2, 3}; int dst[3]; memcpy<3>(dst, src); for (int i : dst) std::cout << i << '\n'; } It's fast because there is no run-time loop!
  15. What do you mean by “destination”? Are you simply looking for a path between every pair of vertices in your graph?   Your example code doesn't clarify your question much either – you iterate over all unordered pairs of vertices, and then what? What would those 16 inner loops do?