Experience of making WPF online flight games

Source: Internet
Author: User
Tags call back

As an early childhood entertainment project, feiqi seems to be quite profound in my memory. It has always been my goal to achieve it by coding. WPF has a comfortable image encoding experience and naturally becomes my first choice. Along with the binding of WPF, a new model also emerged-mvvm (Model-View-viewmodel ), makes the page and logic better separated. However, this experience has been severely affected for me, regardless of technology or ideology.

 

Cause: MVP Mode Discussion

 

Of course, it is still because of the following conversations that make me impulsive to do this game without any preparation:

 

Someone: Are you still using MVP mode?

Me: mvvm is used now.

 

Someone: mvvm?

Me: the WPF binding function is very powerful, and the view and model can be completely separated.

 

Someone: MVPs can also be separated, and presenter can be used as a medium for both.

Me: Do you want to get some DTO from the service and assign values to the control through presenter, register the control events on iview in Presenter (only methods, properties, and events are described below) and call back the service in the event.

 

Someone: That's right. What's the problem?

Me: assume that the object is employee, and there are some common attributes name, ID, and address on it. There may be a dowork method, and the employee may be an abstract class. There are various division of labor below, the dowork method of each subclass is different. According to the object-oriented idea, is dowork on the employee object. Your current practice is that there is a dowork method on the service and the emplyee object is passed in. In this way, the object you pass has no behavior but attributes. It is nice to say that the anemia model is called, actually, the structure is the same in the C language. Functions on objects are called methods, and services are just a collection of functions. The current statement is service-oriented programming, but it is process-oriented.

 

Someone: Yes, It is object-oriented programming that should have behavior on objects. But the mainstream methods are like this: webserivce and WCF. Even if you connect directly to a database, you need a layer of conversion. You will not directly use SQL in the method, even if SQL is used, it is process-oriented because the database is not object-oriented.

Me: Well, we will also use process-oriented operations, but we should avoid process-oriented behavior as much as possible.

 

Someone: In terms of object behavior, convert the DTO obtained from the service into an object on the client, and encapsulate the service that calls the service in the object method, you can call the method of the object instead of directly calling the service in the control event. This is what we do now.

Me: This is true. In fact, even WPF needs to register some events to meet the requirements. However, if you need to display the name of a person on the page, do you want to list controls on iview when you modify the name, and register a textchanged event like presenter, for example, if there are other things such as phone numbers and residential addresses, whether presenter registers events alone becomes bloated, and you need to expose specific controls on iview. If iview does not expose specific controls, however, when some attributes and methods are used, the various iviews of the control operations will gradually become messy. After all, adding methods or properties to values, value changes, and Operation changes, then you will want to directly call the presenter method from the view, but the MVP thought is that the view does not know the presenter, And the presenter can manipulate the view, or else it will become MVC.

 

Someone: This is really not easy to maintain iview. What if I remove iview and let the presenter manipulate the specific view directly?

I: I have another intention to define iview. I just mentioned that I only add attributes and methods to it. The purpose of this process is to make interface changes, so that the Web can be used, and winfrom can also be used. A common method is to place a view in a project, a presenter in a project, and an iview in another project. The view registers an iview through the IOC container. When a presenter starts an iview, the IOC container automatically finds the corresponding view for the instance, so that the presenter and view references are separated. That is to say, if iview is changed to view, presenter and view should be put together. At the very least, presenter should reference the view project. Although it is also an MVP, it feels weaker.

 

Someone: We put the presenter and view together, or even in a folder. The presenter is responsible for the data presentation and transformation on The View, it is to assign the value of the corresponding data changes on the view to the client object through the presenter -- that is, the object transferred by DTO. We put these objects in another project, and you would not think that we would put some logic in the presenter for processing. After all, the interface transformation is only the performance of data, but the object is still in another UI, the object does not know the specific presenter, so it is also considered as a separation. In addition, the attributes and methods are put on the iview you just mentioned, such as whether the click of the button needs to add an event, the event itself is associated with the UI, unless you add some objects to the iview layer to transfer information, you can feel that it is layered for layering.

Me: Can your presenter retrieve data from multiple objects? For example, some of the data on the view comes from object A, some from object B, and A and B are in parallel.

 

Someone: No. Currently, one presenter corresponds to only one object. If this happens, we will cut the presenter into more details.

Me: that is to say, objects a and B correspond to one view, so the C view containers of a view and B view correspond to the C objects of containers of A and B, it can also be said that the C object has a and B attributes?

 

Someone: objects a and B correspond to one view. You are talking about this kind of C view. Sometimes the attributes of objects A and B as C will make the objects bloated; assuming that the generation of object A will surely produce a view, the interface designer should know where the view is docked, for example, if the view is parked in the C view container, there will be a place to leave it in the C container. Our approach is to write a customerpanel similar to a container in scsf, write the regionname of the view that needs to be put on it, and use some containers to make a bridge to achieve this effect.

Me: Can you reach a view in multiple places? For example, the view of the information I modified is in one place, and the new information is in the pop-up window.

 

Someone: Yes. Because the data is different, the presenter of the view is created first. The presenter determines the regionname of the view based on some information of the object. A view has multiple regionnames, of course, a regionname can only appear in one place.

Me: How can we let the presenter know the data changes in the object? For example, if you change the attributes of a, but the attributes of B also change in the logic of A, and B is also a column to be displayed on the view, how do you detect changes to the attributes of B, write an event to register with presenter?

 

Someone: Is there any problem with this?

Me: if it is a common event, there will be a strong reference problem. When your view is closed, the object does not need to be destroyed, so that the object still stores the presenter reference, this may cause memory leakage.

 

Someone: in fact, our object will inherit from a base class. A weak event mechanism is implemented on the base class, that is, the weak reference is entrusted and the weak reference is placed in a list, if the weak reference is not empty, that is, the delegate is triggered. Presenter registers the weak event and receives the notification, similar to the inotifypropertychanged mechanism in winform.

Me: WPF also relies on this mechanism for binding, but it is better than winform. In addition, it manages registered events through weakeventmanager, I feel that the relationship between your view and presenter is only one that does not know the business object.

 

Someone: can this be better used by front-end designers? After all, the Business Objects involve the business logic. The front-end should mainly focus on the UI and UE design.

Me: What you said makes sense. Your design can indeed meet most of the requirements, but front-end designers also need to know some business logic, or else how to design a user-friendly interface. Some of your text assignments, such as names and addresses, actually know the client object directly, which can reduce the workload of presenter. After all, the Design of simple operations such as text change presenter is too vulgar, and the interface designer should also be clear about the effects of operations such as clicking a button.

 

Someone: Our principle is that operations on interface changes should be performed at the view layer as much as possible. If you click a button, a prompt box will be generated. The style and position of the prompt box are controlled by the view layer, the content is granted by presenter. However, as you said, the presenter is heavy and complicated. You need to fully understand the view and client logic layers. But isn't presenter responsible for both. Does mvvm have a better experience?

Me: mvvm is determined by the WPF control mechanism. It can easily implement bindng, which is similar to the binding of winform. The value in the front-end textbox has changed, the corresponding attributes in the back-end class also change, which saves the trouble of assigning values to the presenter, and provides the command mechanism for the control event, that is, the behavior of WPF, in this way, each view corresponds to only one object, with behaviors, attributes, and flesh and blood. However, the disadvantage is that the interface requires a client object. We call this client object viewmodel. To achieve better separation and interaction, we can make the viewmodel an interface. The view only knows the interface, in this way, the view should be clearer.

 

Someone: It seems a little abstract, unique to WPF? Is there any example?

Me: I am just preparing to write a simple game that will open your eyes and achieve it.CodeAbsolutely elegant.

 

Incorrect Design

 

There are several general causes of incorrect design: technical difficulties are not grasped, and business needs are not clarified. of course, there are still many reasons for me. For example, I started school in May September and had a bunch of tests and retake. I had to go to work again during the day, so I wanted to solve it quickly.

 

Error 1:

No network interactionProgramI started to think so. The communication method uses WCF, but the worst thing is that I have never learned it specifically, and I am confused about network communication, it doesn't matter how simple it can be. using TCP or UDP makes it possible to achieve the effect, so that you can learn about WCF only one week before the completion of the program.

WCF can use the svcutil tool to generate a proxy class, or automatically convert the server contract to a client class. If/EDB is added after the svcutil connection, the client class can be generated with the inotifiypropertychanged interface, then, a raisepropertychanged method is automatically implemented to call raisepropertychanged in attribute assignment. This should be convenient for developers. Generally, do not modify the generated class as much as possible, because automatic generation can be more convenient when the server side changes, otherwise, it would be too troublesome to modify this "Bulk Header", but sticking to this practice also makes me tired, for example, I originally wanted to put a dictionary in the room to serve as a seat and then put the player in it. because the number of visitors is not considered, I can set the dictionary to four, then, you can use a for loop to determine whether there is anyone in the seat (the reason for not using the list is thread security). If seats [0] = player UI, it will not be refreshed, because propertychangedeventhandler is not triggered, seats is the attribute unless it is the action of seats = seats, but even so, the automatically generated code is as follows:

 

PublicSystem. Collections. generic.Dictionary<Int, Modelmodule.Player> Seats {Get{Return this. Seatsfield ;}Set{If((Object. Referenceequals (This. Seatsfield,Value)! =True)){This. Seatsfield =Value;This. Raisepropertychanged ("Seats");}}}

 

As you can see, the path that you assign to yourself does not work, and I have to write it in partial class.

 

 /// <Summary> ///  Player is not empty. Specify a player in the seat or remove the player.  /// </Summary> /// <Param name = "seatno">  Seat Number  </Param> // <Param name = "Player">  Players  </Param>  Public void Setseat ( Int Seatno, IPlayer Player) {seats [seatno] = ( Player ) Player; If (Player = Default ( Player ) {Removeplane (seatno);} raisepropertychanged ( "Seats" ); Application . Current. Dispatcher. begininvoke (( Action ) Commandmanager . Invalidaterequerysuggested );}
 
 

If you design it again, I must add the seat concept. The corresponding attribute in player is not the room but the seat. The seat knows the room number and the seat number. I am a player now. The attribute is room and the attribute is seatid. This design is the most considerate of me now.

 

Error 2:

The confidence in WPF is too high, and it is believed that WPF can achieve faster sending. In fact, using mvvm requires a large number of custom controls, a large number of attachbehaiver classes, and a higher design idea is required to separate viewmodel. One of the worst designs can make everyone despise me is that it is not for the server to determine the outcome of the battle, but for the client to tell itself that the client wins or loses. One reason for this design is that I didn't want the client and server to use the flight logic each time when I first died, or the server should generate a grid to be transmitted to the client, the client only displays the UI. At that time, it was well done. It was nothing more than converting the coordinates into a path, and it was good to run it with matrixanimationusingpath. But the problem is that one plane is about to fly over another plane. The timing must be accurate and it is useless to tell the coordinates, that is to say, it is too stupid to transmit the boxes on which the planes are eliminated and implemented using the key frame method. It took a long time, because the time spent was too long, the current method is used, and the client accepts the number of workers on the server to run. Because it is too fast to run a grid in the class, I have to use autoresetevent, etc, this should be the most uncomfortable thing in my career. If I try again, I will make sure that the server executes the flight logic, and the client also executes the flight logic (only accept the number of records given by the server, saving transmission resources ), however, the client's flight logic is only to generate flight routes. In the other logic, it is used to determine whether an aircraft has collided with another aircraft. Therefore, the flight animation effect does not need any built-in storyboard in compositiontarget. you can use the rendertransform attribute in rendering. Because the animationtimeline class is used, some of your presentations won't be captured. It is too expensive to register his currenttimeinvalidated event. We cannot control the clock of the animationtimeline, when the plane was supposed to fly over, there was a tail gas effect. It took a lot of effort to achieve the desired effect.

 

Overall game design ideas and practices

The design of the hall does not have as many areas as the game hall on the market, such as the new hand area and master area. Because the intention is only a demo, you can run it in the LAN environment, so players can only see one hall when they open the screen. I named the hall area. Because the area only requires one instance, it runs in sington mode on the server.

There is room (room) in the area, the default number is 50, the number of rooms should be the number of rooms that will not change after the area is created, I put it in the Area constructor for initialization, player (player) an ID (guid) is automatically created when the client instance is used. Call the enter method of the server to enter the hall and obtain the room on the server. The room structure contains players in the room.

Why don't all the people entering the hall be returned, but only the room?

At that time, it was of little significance for players to see the people entering the hall. This simple game did not include the in-room chat function. You only need to let the players know which rooms are sitting in the room. If a few people raise their hands, it is enough to determine whether the room starts to play games.

The enter method of the server not only saves the player's basic information, but also saves the operationcontext of the player. current. getcallbackchannel <iplanechesscallback> (). To ensure security, check whether the communication channel is consistent every time the client sends a request.

The client calls the server method enterroom to enter the room. At this time, the server should determine whether there are already people in the seat. If someone else has an error (poor, I still don't know how to handle the exception information of WCF ), when you enter the room, you should notify everyone in the hall. operations such as raising your hand, exiting the room, starting the game in the room, and ending the game in the room should also be notified.

When gamers entering the room raise their hands (more than one player in the room), the game starts. In the circle after the game starts, the system immediately generates the number of hits and only needs to notify people in the room.

The client selects an airplane and sends the plane to the server. The server sends an excellent sub-notification to the person in the room. If no action is performed within a certain period of time (20 seconds by default), the client selects a plane and submits it to the server.

When the client finds that the game is complete, it notifies the server that all people entering the Hall have ended the game in the room.

When the server fails to send the information to the client, it first checks whether the player is in the room. If there is a room, it exits the room and then deletes it from the system's personnel list.

 

Project Structure Design

Server: host is the Startup Program, and service is the entity class and service class.

Client: wpfclient is the Startup Program

Controllibrary is the WPF control library.

Common is IOC and reading app. cofig.

Viewmodule is the interface

Modelmodule is viewmodel.

It is worth noting that in wpfclient, I did not add reference to viewmodule and modelmodule.

In the configuration file

 <  Modules  > <  Module  Assemblyfile  = " Viewmodule. dll " Moduletype = " Viewmodule. module, viewmodule " Modulename  = " Viewmodule " > </  Module  > <  Module  Assemblyfile  = " Modelmodule. dll " Moduletype  = " Modelmodule. module, modelmodule "Modulename  = " Modelmodule " > </  Module  > </  Modules  > 

This is a module practice for learning the prism framework. I have made some improvements for the project. After reading the Assembly, I will check whether there are classes inherited from the imodule in this Assembly, if yes, the class of the instance and the Register Method in the imodule interface are called to register the corresponding class to the IOC container.

However, modelmodule and viewmodule are not referenced in wpfclient. If you want to read their assembly, you still need to copy the DLL to the program directory corresponding to wpfclient. Run the following command:

Xcopy "$ (targetdir) *. *" $ (solutiondir) wpfclient \ bin \ $ (configurationname) \ "/y

Why do we need to work hard? What is the significance?

This is to form modules. I originally wanted to make the chat in the room into another project, and then I could build it as a plug-in. That is to say, leave a place on the original view, my chat view can be put in through a certain container. The practice in prism is to leave a region, and then write the regionname on The View, in fact, the regionname container is not necessarily a single container, but a multi-container such as menu, ListBox, canvas, and stackpanel. Naturally, you need to write an adapter for such a multi-container.

 

Result: Reflection

First, let's talk a little less without full control. Even if you are sure, you should keep a low profile.

If the design is not based on the idea of software engineering, the whole process should first design the class, then design the interface, then modify the class, and list the detailed schedules in each step, allow the most abuse of methods to be completed first, and time can be modified. Taking too long at a certain point will often lead to no better thinking in future design. Therefore, the previous shortcomings can be corrected unless they have been completed or wrong, at least get a version first. When doing this, you have a thorough understanding and a more comprehensive consideration. Of course, you should keep up with the test after the class design is complete. I remember the C ++ rumors circulating on the Internet when I first learned programming. Now I feel more and more like he is talking about truth. C is not c ++, and C ++ is not C #. It can be said that the combination of C and C is completely different. The characteristics of the language make the design idea completely different.

Some of them are too technical, such as storyboard. Today, when I was on a bus, I saw an advertisement for an animated film. One of them was impressive: cartoons were dominated by stories, and 3D technology was used to make the stories more vivid. For programs, built-in controls are used only for simplicity, not for cool purposes. For more information about the performance method, see explain.

For the mode, mvvm is not as good as MVP in terms of the degree of UI detachment, at least so far, because the command binding and so on all need to be bound with WPF. The mvvm mode also limits the interface to one type of WPF. Even if you use a framework like Caliburn that does not use command to partially decouple, it is still difficult to completely separate it from WPF.

In addition, the game had to be stopped because of the long delay, so the practice in many places was very hasty. This game can be said to have completed only a small part, it can even be said that it was just an attempt at the beginning, maybe it will be finished later. Of course, it is estimated that it will be re-launched. Whether it is class design, Xmal, or the aesthetic of the page, it has made me unable to endure.

PS: not just this piece of work, it has become increasingly difficult to understand the original design. I want to change everything, and I want to redo everything.

Finally, I would like to thank Meng and his wife for providing the token.

 

We still provide the source code, hoping to use it for some people.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.