Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Evan Bowman

Pages: 1 [2] 3 4 5
16
SFML projects / Re: My first SFML Program
« on: August 15, 2016, 09:36:54 pm »
memory management is boring  ;D

Subjective  :)

17
SFML projects / Re: My first SFML Program
« on: August 15, 2016, 05:54:04 pm »
Quote
Pushing the actual sprites would be my choice though; they're tiny and most likely don't need to be on the heap.

The header information of a vector is stored on the stack, while the elements that you push back are still allocated in free store (i.e. on the heap). But of course using a vector is way better than performing heap allocations for single objects with operator new.

Quote
There doesn't seem to be any benefit to using std::array instead of std::vector here. Both are equivalent really with vector having more flexibility if something changes.

True, std::vector is indisputably more flexible, the only reason I brought it up is that using an array performs allocation for the contained elements all at once upon construction, while using std::vector::push_back(const & value_type) calls additional copy constructors for each call to push_back(), and you take a performance hit each time vector doubles in size upon reallocation. If you know before hand that you're pushing back at least three elements and are using a vector for future flexibility, it might be worth calling the fill constructor rather than calling push_back() three times, or if you do want to call push_back() first calling std::vector::reserve().

18
SFML projects / Re: My first SFML Program
« on: August 15, 2016, 02:11:14 pm »
Is there a reason for the double asterisk (**iter)? ??? Nevermind. I just realised that your container stores pointers and not sprites ;D

If you're using C++11 or later, you can draw your container of pointers much simpler:
for (auto& sprite : spriteContainer)
    window.draw(*sprite);
(if your container stored actual sprites, the asterisk wouldn't, of course, be necessary)

Worth considering that this creates an lvalue reference to each element (8 bytes on a 64 bit machine), where each element is a pointer (the exact same size), so getting the elements by value would be just fine here.
(click to show/hide)
Some better alternatives to using new and delete would be using smart pointers (look at std::unique_ptr), or just pushing back the actual sprites. In fact, since you know the size of the vectors at compile time you may be better off using an array. std::array is nice and bounds checked, and also compatible with range based loops :)

19
Hi,
I've still been actively working on this, and thought I'd share a brief demo of what I've completed so far. During the past few months I've been working a lot more on the code parts--I started the project as a way of learning C++ and making my first computer game, and after a few months it had grown into something really unmaintainable. I meant to demo it earlier but at one point I ended up scrapping about half the code and the whole project hasn't been stable enough until now.

I have a lot left to do, and a few things in the current demo are unfinished:
 - Critter enemies (the red guys) don't have an attack yet
 - Chests don't contain items, I'm planning on having opening a chest grant the player certain timed effects that wear off
 - The intro scene has a lot of the features disabled (the door is locked open, you can shoot through walls).

macOS version here:
https://www.dropbox.com/s/9evoceraioqsgza/BlindJump.app.tar.gz?dl=0

It is also Linux compatible, but I'm not sure exactly how to distribute software in linux as a simple package... in the meantime:
Code: [Select]
git clone https://github.com/evanbowman/Blind-Jump.git
cd Blind-Jump/src/
make depend
make Linux CPPFLAGS=-DLINUX
./BlindJump # <-- This runs the executable
Would a tarball containing the compiled executable be easier?

The only dependencies for building the project are SFML 2.3.2 and a C++14 compatible compiler, so in theory it should be possible to compile it on windows. I have no idea how stuff works in that environment though, so I have no idea how difficult it would be... but if you want to try compiling it: https://github.com/evanbowman/Blind-Jump

Enjoy, and let me know how I can make it better!

Edit:
Oh yeah I forgot, the controls:
x - shoot
z - interact/dash
arrows - move
esc - menu (items don't change yet when you select them, but the quit option should work, it just won't tell you that it's selected :) )
Should also support playstation controllers out of the box. If you have a controller you'd like supported, feel free to add it to this file and create a pull request: https://github.com/evanbowman/Blind-Jump/blob/master/src/inputController.cpp (in the function InputController::mapJsById())

20
General / Re: [Q] Traverse a Binary Tree
« on: July 04, 2016, 05:13:17 am »
Quote
I'm assuming because my teacher has never mentioned ctor to us, that I don't have to use that style of initialization?

I guess there's a lot going on here that I should elaborate on. ctor is a common written abbreviation for 'constructor.' In C++, there are three ways of initializing a variable:

Code: [Select]
int var1 = 0; // Assignment style
int var2(0); // Constructor style
int var3{0}; // Brace style, post C++11 only. Compile your code with the flag -std=c++11 if you are using gcc or clang

If you are using C++11, it is usually recommended to initialize most things with braces, with a few exceptions. This is because among other things, braced initialization does not allow narrowing, which is a term for the loss of data that occurs when you try to initialize a variable with a larger value than will fit.

Quote
Also, is that line of code just putting a null value into the left and right child?

That's correct. The list of members following the colon after the constructor name is an initializer list. Initializer lists are the preferred way of initializing a class or struct's members. You can see from the above description of initialization methods that each member is being initialized to nullptr by braced initialization. Constructor style initialization would also work here (but the assignment operator (the equals sign) would not).

Quote
As for the traverse function, that's just identifying if a left and/or right child exists? So if I wanted to do preorder traversal, I believe that prints out all the values in the left side of the tree first, and then the right, right? So, if I had, lets say, five nodes on the right side of the tree, and four on the right, would I just need a variable to keep track of the amount of nodes on each side of the tree, and then just loop the traversal by that value?

The traverse function checks if the left or right child exists, and if it does, it calls traverse on that child. If you were to call traverse on the root node (the initial node at the top of the tree with no parents), the function would visit all of the nodes in the tree. No looping or variables needed here, this function already does a preorder traversal. Stepping through the code in a debugger might be helpful in understanding what it's doing.

Quote
As for the main function, I've never actually put anything in the "(" ")" before, could you explain that to me?

Do you mean the char ** argv? That is called an argument vector. The argument vector is an array containing strings that are passed to your program as parameters. argc is the length (number of strings) of the argument vector. argv[0] by default is your executable name. The reason these parameters exist is so that you can pass arguments to your program via the command line. Say that you saved the example code I posted as a file called example.cpp and then compiled it to an executable like this:
Code: [Select]
clang++ example.cpp -std=c++11 -o example   // clang is a compiler, you could also use gcc/icc/whatever you have
This would generate an executable in the same directory called example (I believe if you are on windows, the executable would also have an extension, .exe).

Then if you ran the executable with some parameters...
Code: [Select]
./example some parameters here  // I don't know how to run an executable on windows from the command line, but you might not need the ./
The argument vector would contain:
argv[0] = "example"
argv[1] = "some"
argv[2] = "parameters"
argv[3] = "here"
argv[4] = NULL <-- A null terminator, not included in the argument count

If you're interested in this and you're working in a unix based environment, the standard ways of processing command line arguments are either doing it yourself, or using getopt:
https://www.gnu.org/software/libc/manual/html_node/Getopt.html

Quote
Thanks again for your help and support man. Seriously means a lot to me!

No problem!

21
General / Re: [Q] Traverse a Binary Tree
« on: July 01, 2016, 04:26:07 am »
I've never worked with threads before, so, this would be useful to preserve how much work you put the computer through? Kind of like.......optimization? Thanks again for helping me man! You broke this down for me, and I don't know how to repay you!

No problem. The computer actually would be doing the same amount or slightly more work, but it could potentially do the work faster because it has broken the problem down into smaller parts, and can do each of these smaller parts at the same time (any threads you create can be run a different non-busy processor core, if available). Creating and joining threads does incur a small performance overhead though, so this is more likely to be faster the larger the tree is. I just thought it was worth mentioning, because in the past decade processors haven't gotten that much faster, but they are more capable of running things (eg threads) in parallel, as a consequence of having more cores.

Some problems lend themselves to parallelization, while others do not. Parallelization requires steps to be independent because threads can start in any order and run at the same time. For example, traversing binary tree subtrees can be considered independent. If you were to traverse the right subtree before the left one, or do them both at the same time, it wouldn't make a difference, because there are by definition no dependencies between the two subtrees.

Here's a famous article about concurrency and why it is increasingly relevant:
http://www.gotw.ca/publications/concurrency-ddj.htm

22
General / Re: [Q] Traverse a Binary Tree
« on: June 30, 2016, 05:00:59 pm »
Also worth considering that DFS can easily be done in parallel (manually unrolled first stage for readability, although it would be more extensible if done programmatically based on detected hardware parallelism, also this example doesn't account for all possible corner cases).
Code: [Select]
void beginParallelTraversal(TreeNode * pNode) {
if (pNode->leftChild) {
std::cout << "Found left child" << std::endl;
std::thread worker(traverse, pNode->leftChild); // Do half the work on a separate thread
if (pNode->rightChild) {
traverse(pNode->rightChild);
}
worker.join(); // Wait for the other thread to finish
} else {
traverse(pNode);
}
}

23
General / Re: [Q] Traverse a Binary Tree
« on: June 30, 2016, 04:31:40 pm »
Quote
Every other example I found was using Classes, Stacks or Queues, and I haven't learned those yet. So, having this as a foundation is going to make it a lot easier to build upon it, to more advanced methods of doing this.

It's worth noting that while recursion is visually elegant, it does also use a stack, only it's abstracted behind function calls (each call pushes a new stack frame, which are executed in last in first out order (LIFO)). The function calls result in slightly more memory usage, but if you're not doing too much work inside the function and a reasonable number of iterations you won't see a performance difference.

When you learn more about data structures stacks/queues will make more sense, but starting with a recursive implementation can help to understand what is going on at a high level (which is why the psuedo code for many algorithms is written recursively). Converting a recursive algorithm to a stack based approach is usually fairly straightforward, after you've done it once it all makes sense (non recursive merge sort would be a fun challenge, and a useful thing to know how to do).

Quote
thank you so much for your time and help Evan!

No problem :)

24
General / Re: Executable bundled into mac app package crashes
« on: June 30, 2016, 03:36:28 pm »
Thanks for your help with this  :)

25
General / Re: [Q] Traverse a Binary Tree
« on: June 30, 2016, 02:23:46 pm »
Binary trees are fairly simple, in this case even simpler since your structure doesn't need to fulfill the search tree property. They're really just a collection of nodes where each node has at most two children.

So for example, a tree node:
Code: [Select]
struct TreeNode {
TreeNode * leftChild, * rightChild;
TreeNode() : leftChild{nullptr}, rightChild{nullptr} {}  // Change to ctor style initialization if you're not using c++11
};

Then to traverse, you could simply start at the root (the initial node), and explore it's non-null children, children of children, etc.

You might want to look into existing graph traversal algorithms, Breadth First and Depth First traversal are fairly simple.

Of course, an easy recursive solution is fine if your tree is reasonably shallow.
Code: [Select]
//...
void traverse(TreeNode * pNode) {
if (pNode->leftChild) {
std::cout << "Found left child" << std::endl;
traverse(pNode->leftChild);
}

if (pNode->rightChild) {
std::cout << "Found right child" << std::endl;
traverse(pNode->rightChild);
}
}

int main(int argc, char ** argv) {
TreeNode node1, node2, node3;
node1.leftChild = &node2;
node1.leftChild->rightChild = &node3;
traverse(&node1);
}

The above is a variant of depth first search (adapted for binary trees).

Arrays can be a more compact way of storing a binary tree, especially if tree nodes contain only numerical values, but methods of traversing them when stored this way are not immediately obvious (require math), so I showed it with structs.

26
General / Re: Executable bundled into mac app package crashes
« on: June 27, 2016, 01:19:38 am »
So the build scripts were all fine, and I've narrowed the issue down to my implementation for resourcePath().

Code: [Select]
std::string resourcePath() {
const static std::string str("../Resources/");
return str;
}

I implemented my own because I thought it would be more portable, but I guess something more complicated is going on in the background than going up one directory and looking in the resources folder. What exactly does the obj-c version do? When running an executable from a mac package, does the executable somehow get moved outside the package when run, thus invalidating my hard-coded path?

27
General / Re: best practice
« on: June 21, 2016, 01:55:26 pm »
Quote
Quote
Also, isn't tracking the events manually objectively faster? At least better than doing a system call for key presses for every iteration of the main loop in the 30 odd places I need to know if a key is pressed.
It certainly is faster. But I doubt anyone will get performance issues with this stuff ;) At this level, it's better to use the cleanest/simplest solution (which is events for you anyway, because of the bug you explained).

Yeah I agree, I've found horribly inefficient old code while refactoring some things lately, but if I'm not careful about limiting draw calls, things slow down right away :)

Quote
There's no issue, it was just a design question :)

Sorry about that, I took it a bit too seriously.

28
General / Re: best practice
« on: June 21, 2016, 08:32:22 am »
Quote
this is how I typically use event loops
Leaving the InputManager handle the whole event loop is questionnable. What about non-input events?
And it's also not clear why you track key press/release events manually and not use sf::Keyboard/sf::Mouse.

First, I don't use sf::Keyboard/Mouse because they don't work reliably on all operating systems. For example, for a fullscreen sfml app running on a 2014 macbook air and OS 10.11, if the computer suspends, upon waking sf::Keyboard/Mouse become unresponsive, but events still work. Maybe this is an isolated incident, but I would still be apprehensive about using them.

Additionally, I am defining all events as input here. Events would not appear to occur if a user were not interacting with the host computer, or a process were not interacting with the application, therefore events are arguably input. But that's more of a semantic thing.

I guess what I'm trying to say is this; I don't see the issue with moving the event loop from the main loop to a function called at the beginning of the the main loop. There is no significant difference.

Also, isn't tracking the events manually objectively faster? At least better than doing a system call for key presses for every iteration of the main loop in the 30 odd places I need to know if a key is pressed. Additionally, having a class wrap sfml input functions makes it easier for me to add joystick support. Rather than check for a joystick button and a key press everywhere, I can handle that internally in a centralized location, and have the input manager's accessor functions correspond to the current selected input device.

29
General / Re: best practice
« on: June 21, 2016, 03:53:14 am »
Say you were to move w.update(...), a function not having to do with events, into the event loop. It may be updated multiple times, or not at all, which most likely isn't what you want. My understanding is that control flow only enters the body of the event loop when a valid I/O event occurs (such as a key press/release, etc.). If you have an object or variable that records keyboard states, then that would be a good candidate for updating inside the event loop. Otherwise I wouldn't really mess around with it.

EDIT: this is how I typically use event loops, inside some sort of wrapper class:
(click to show/hide)

30
General / Re: Executable bundled into mac app package crashes
« on: June 16, 2016, 07:59:48 pm »
Hey thanks, I'm in the process of doing a lot of refactoring to my older code for this project right now, and one of the things that I'll be looking into is management of textures. I believe that all the file paths are correct, but I'll check to see if this is the problem.

Another thing--I wasn't aware of symlinks in the package bundles, that could be the problem too.

I guess if none of that works I could just continue working as is with emacs/makefiles and use Xcode/VisualStudio/dpkg to create the app bundles when I want to release it.

Pages: 1 [2] 3 4 5