Jump to content

Roland

Members
  • Posts

    2,953
  • Joined

  • Last visited

Everything posted by Roland

  1. So here is a small example of a game with a first person player with a camera. You can move the player forwards, backwards and turn him around. The camera will detect any items in front of the cursor within a limited range. If you left click on such an item when its detected it will go away, add health to the player and update a health meter showing current health. Here is a screenshot of the example (here with two different kinds of items to pick and two meters, but besides that its the same) Instead of diving into coding the player with all its operations to handle we won't in fact make any player coding. Instead we will divide things into components with their own responsibilities. The components is hold together by a runtime object created by LCS and is nothing you don't need to care about. So what is a component then. Its a LUA class that has Events and Actions and is completely isolated and independent of other components. This means it can easily be replaced with another component that behaves in another way. It can also without any changes be used in other projects. So how would we go about this then. First of all we have some entity's in our scene. Health items to pick up (the boxes) Player (a simple invisible cylinder) including a FPS camera HealthMeter ( GUI showing current health) Normally we the would have started making Scripts for those items above. But were not this time In fact none of the Entities in the scene will have a Script. Instead we are going to write Component scripts that can be used here or in some other game. So what components could our player need then? That's up you depending on how you want to design things. Here is a suggested approach by me. Input component that handles user input Controller component that handles movement of the player and camera Health component that keeps track of current health Cam component which is the FPS camera Further on the Health item needs a HealthItem component that reacts to a pickup and a HealtMeter component that serve as a GUI for current health. You don't need to write any Script code for the Entities in the Scene, only component needs some action by you. Each entity will be connected to a GameObject (which is generated in runtime and does not need any coding). This GameObject ties the components together and handles all things to get the communication going. The system is defined by supplying a setup written in JSON format. However you wont need to do that either as I'm currently working on a design application for setting things up and that will be available when we release the system. Anyway.. so now when we know what components we need, its time to think a bit on what they should to and how they will interact with the surroundings. So lets get started. I won't go through every component but I think you get the idea Input Should handle input from the user and generate events accordingly. An event can be subscribed to by any other component and sends some information about the event that happened. All events starts with 'on'. A subscriber of an event must have an action that will be triggered by the event. All actions starts with 'do'. Events onForward is sent when user presses 'W' onBackward is sent when user presses 'S' onClick is sent when user clicks left mouse button Controller The controller is responsible for moving the player Events onMove is sent when the player moves (contains information about current position) Actions doMoveForward moves the player forward doMoveBackward moves the player backward doTurn turns the player around its Y-axis (needs an angle) Cam Is simply the camera which is rotated by moving the mouse. Events onPick is sent if the doClick action is called when an pickable entity is in front of the camera. The entity is sent with the event. onTurn is sent when the user rotates the camera by moving the mouse Actions doMove moves the camera to a given position Here is a drawing showing our system now. The yellow parts are entities in the scene (map), the red ones are GameObjects created in runtime holding the components and finally the blue ones are the components which we have to write code for. L Lets have a look at the code for one of our components to see what it looks like The Controller class must have following functions to be treated as a controller by the LCS system init called by LCS to create the controller attach called by LCS when its attached to its entity and GameObject Besides those two, a component can (optionally) have following functions called in the same way as in normal Scripts. If one or more of those exist they are called automatically. update called on UpdateWorld updatePhysics called on UpdatePhysics draw called on Draw drawEach called on DrawEach postRender called on PostRender detach called on Detach In the init function you can see how an Event is created. That is all needed to create an Event. self.onMove = EventManager:create() If you then look in the update function you can see how the event is sent by calling 'raise' self.onMove:raise({Pos=newpos}) This event a contains a position. All event information are sent as tables. In this case a table member Pos set to the current position. Further there are some actions that can respond to events from outside world. Lets look at one of the actions function Controller:doMoveForward() self.move = self.moveSpeed end The action is just a normal function that may or may not have an argument. It does not need to return anything. One exiting thing is that all Actions are automatically executed as coroutines which has really cool advantages. I wont go into that here but I thought its worth mentioning. Here is a code snippet from the Cam component showing an action that can handle an onMove event that has an argument. function Cam:doMove(args) self.camera:SetPosition(args.Pos,true) end The args is the table sent with the event ( remember self.onMove:raise( {Pos=newpos} ) ). The args table will contain the member given when the event was raised. Okay. Lets see how all this works then In the diagram above you can see the components and the events in action. User presses W and Input:onForward event is sent to the Controller:doMoveForward action which make the player move forward The Controller raises the onMove event which is received by the Cam:DoMove action which moves the camera User presses S and Input:onBackward event is sent to the Controller:doMoveBackward action which make the player move forward The Controller raises the onMove event which is received by the Cam:DoMove action which moves the camera User moves mouse left and the Cam:onTurn event is raised giving the angle of rotation the movement caused. This event is caught by the Controller:doTurn action which rotates the player in the same angle User left clicks the mouse which raises the onClick event which is caught by the Cam:doClick action. If the camera now has an item directly in focus it raises an onPick event which then is caught by the entity's component HealtItem:doPicked. In doPicked the HealthItem will hide it self and send a message back to the player telling that it needs to 'add.health' among with the amount of health. The message will end up in the Health component (by magic ) and the health will be increased. As the health has changed the Health:onHealthChange event will be raised and is caught by the HealthMeter:doSetHealth action which will show the new health The communication between Events and Actions is set as hookups. A hookup means that a component subscribes to an Event. Take our onMove/doMove example above. Setting up a hookup for this is simple self.Controller.onMove:subscribe( self.Cam, self.Cam.doMove ) But that's done for you by the LCS system in the runtime GameComponent holding the Components for the player. How, you may wonder? This is done by creating a LCS project file (in JSON format) defining the GameObjects, Components and Hookups. Going into this in detail goes a bit out of scope for this 'short' description, but I can show a cut from that file { "source": "Controller", "source_event": "Move", "destination": "Cam", "destination_action": "Move", "arguments": "", "filter": "", "post": "" } This creates the hookup between the Controller and the Cam. This is only a simple example but you can add functions right here also for adding more arguments, making event filters and more. Code creation. One nice feature is when adding a new component by defining it in the LCS project file, LCS will create the LUA file for the component with everything added. The only thing you will need is to add some code inside the Action functions. As a last thing I will show how easy it is to take advantage of the actions which are coroutines (by magic). Here is how I raise the health level in the HealthMeter to be smooth function HealthMeter:doSetHealth(args) local value = 1 if args.Health < self.health then value = -1 end local goal = Math:Round(Math:Clamp(args.Health, 0, self.max) * self.valueSize) while self.pixelValue ~= goal do self.pixelValue = self.pixelValue + value coroutine.yield() -- give system some time end self.power = args.Health -- done! end Hope this have given you some more insight into the LCS system. PS: Some details has been left out here to increase readability
  2. Really wonderful to see.
  3. I can make one. Give me some hours An FPS player that can pick health items and have a health meter
  4. Great improvement and finally fog. Thanks
  5. In your Main.lua change context:Sync(true) to context:Sync(false)
  6. Thanks for the update and I just say GO GO GO
  7. Roland

    Forum Update

    Looks great and finally a descent code presentation
  8. Lucky you to see such a beautiful face now and then ?. Anyway I hope this finally means that our code indentions will be treated with respect and dignity
  9. TAB seems to be 8, think 4 would be better
  10. The reason why remove is slow is that all non-removed item are shifted towards the front of the list while the container keeps it's size. After the operation remove returns an iterator past the last non removed item. Removed items are actually not removed​ but just marked as removed. The idea is to bring down the amount of memory allocations and get a continuous vector. You can also use remove to remove all items of a certain value from the vector Here's some notes on iteration on a vector http://fastcpp.blogspot.se/2011/03/fast-iteration-over-stl-vector-elements.html?m=1 There are quite a few types of containers, each one suited for different situations and with their drawbacks in other situations. There is no "the fastest" I'm afraid.
  11. Hard to say without seeing how Link, LinkedList and LinkNode looks like.
  12. Error in C++ - ElseIf int n = 2; if (n == 1) { Print("n equals 1"); } elseif(n == 2) { Print("n equals 2"); } elseif(n == 3) { Print("n equals 3"); } should be int n = 2; if (n == 1) { Print("n equals 1"); } else if(n == 2) { Print("n equals 2"); } else if(n == 3) { Print("n equals 3"); }
  13. Yes using a reference is of course more efficient
  14. Unfortunately I think it's a Microsoft thing, maybe some Linux user can correct me if I'm wrong on that one.
  15. template <typename T> class Link { public: Link(); ~Link(); Link* next; Link* prev; Link* list; T value; }; int main() { Link<int> link1; link1.value = 10; Link<std::string> link2; link2.value = "Hello World"; return 0; }
  16. Well great then If it works it does. So problem solved
  17. Just some free thoughts on this If you are after most efficiency use either std::vector or simple arrays. Using std::list or std::map is not at all suited for games as iterating over them is quite slow compared to vector If possible store the objects and not pointers to them in your array in order to get contiguous memory. Iterating over any data in non-contiguous memory imposes a lot of cache misses in general and removes the ability for the compiler and CPU to do effective cache prefetching. This alone can kill performance When removing from your vector use the 'swap/pop' method. This way you keep your array contiguous std::swap(entitys[index], entitys()); entitys.pop_back(); This of course assumes you know the index of your object. This can be known this way myArray.push_back(object); object.index = myArray.size()-1 then you removes it with std::swap(myArray[object.index],myArray.back()); myArray.pop_back();
  18. Unfortunately I have no idea how the remove work behind the scene.
  19. Node need to have iterators dangling around world->entities.push_front(this); // or world->entities.push_back(this); //... //.. std::remove(world->entities.begin(), world->entities.end(), this);
  20. Hi there Josh. First of all the interator sample has an error std::vector myvector = {"Bob", "Jane", "Fred"}; for (auto it = myvector.begin(); it != myvector.end(); it++) { std::string element = (*it); Print(element); } should be std::vector<std::string> myvector = {"Bob", "Jane", "Fred"}; for (auto it = myvector.begin(); it != myvector.end(); it++) { std::string element = (*it); Print(element); } secondly you can create the vector and iterate in a less mysterious way if you don't care what myvector actually is. auto myvector = { "Bob", "Jane", "Fred" }; for each( auto element in myvector ) { Print(element); } if you do care its std::vector<std::string> myvector = { "Bob", "Jane", "Fred" }; for each( auto element in myvector ) { Print(element); }
  21. Yep. Started with "No knowledge of C++" then covered most things. Classes, Inheritance, Polymorfism, Standard Templates and so on ending up in some Leadwerks scene samples. To bad they are lost and I have no own backup either as I bough a new pc since then and AGAIN!!! was to lazy to make a DVD backup as I had it on YouTube...
  22. Yes I did. But then I switch to a new account, deleted the old one. Of course I forgot to backup them. Yes! Totally idiotic but that's what happened. About 20 videos about C++ lost Sorry about that
  23. aiaf, on 17 May 2017 - 08:35 PM, said: Im interested to do this. Was interested but I guess there will be the same problem for me
×
×
  • Create New...