【Game client】Make you play I guess Like gameplay

【Game client】Make you play I guess Like gameplay

 

      Hello everyone, I'm Lampard Yuanfen~~ "You draw and I guess" I believe everyone is familiar with it. At the beginning, this small game was a must-play after dinner, and it was always in the limelight. Today I want to share with you how to implement a game where you play I guess Like.

      We can simply split the requirement into two parts : 1. Realization of drawing function, 2. Data transmission method

      This article does not post code but only talks about design ideas, I hope it can help everyone~

(1) Realization of drawing function

(1) Canvas

    To draw, we first need a canvas, which we can display with an image control in the UI editor . But you need to pay attention to two places:

    First of all, don't use too dark a color as the background color (you haven't seen a black canvas, I didn't say colorful black...),        and remember that the transparency of the canvas is best set to 255 , because later The drawing is mixed with the target color and the canvas, so if the transparency is not 255, the rendered result may be different from the set brush color

 

(2) rendertexture class

    The next step is to introduce the class that we implement the drawing logic: RenderTexture class , how to understand this class? You can regard this class as a blank paper. If we want to achieve the drawing effect, we must first draw the background color (canvas color) on the white paper, and then continue to add various colors based on the canvas color. the graph we want to draw

    At this time, Xiao Ming, who has a lot of questions, will ask: why not directly use the image imported by the UI manager as the real canvas? What is the concept of blank paper?

    This is because we want to realize the mixed rendering of the place where the brush strokes and the canvas , and the RenderTexture class supports this function, but the UI class Image and Sprite encapsulated in the UI editor do not support it.

    The bottom layer of the RenderTexture class is to call the blending drawing method of OpenGL's glBlendFunc. We have done research on RenderTexture before.

function clsWidgetScratch:initRenderTexture()
    local renderTexture = cc.RenderTexture:create(self.width, self.height)
	self:addChild(renderTexture)
    renderTexture:addChild(self.drawNode)
    renderTexture:addChild(self.rubberImg)
    return renderTexture
end

    If you want to know how to call RenderTexture for mixed drawing in cocos , you can read this article I wrote before: [Game client] Realize the scratch effect

    If you want to understand the underlying openGL rendering logic of RenderTexture , you can read this article: glBlendFunc color mixing

 

(3) Brush DrawNode

    Just now we have created a Rendtexture object, and then rendered the color of the canvas, and then we only need to create the content drawn by the brush, and then mix and render with the canvas to realize our drawing function

    We know that drawing needs to go from point to line to surface, so we can instantiate a DrawNode object with color to draw a point under the brush point

    If you want to draw different colors, you only need to adjust the color field in the drawSolidCircle interface

function clsWidgetScratch:initDrawNode()
    local drawNode = cc.DrawNode:create()
	drawNode:setVisible(true)
    drawNode:setBlendFunc(gl.ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
    drawNode:setLocalZOrder(1000)
	drawNode:drawSolidCircle(cc.p(self.length / 2, self.length / 2), self.length / 2, 360, 10, 1, 1, self.color)
    return drawNode
end

 

(4) Adjust the drawing position

    With the points drawn by the canvas and Drawnode, the next thing we need to do is to monitor the position drawn by the player

    First of all, we need to create a Layout for monitoring and put it on the top layer , and use the onTouchMove method to monitor the position touched by the player. Then set the position of DrawNode to the position where the player writes , and then call the mixed drawing method of renderTexture in cocos

 

(5) Eraser function

    There is an eraser function in the drawing requirements. In fact, smart friends have discovered the so-called eraser function. We can simply set the color of DrawNode to be the same as that of the canvas to overwrite the previously drawn content

    Ok, let's take a look at the effect achieved so far:

 

(2) Choice of data transmission method

    When I saw that the gameplay effect was realized, I was as ecstatic as everyone else, thinking that this demand was coming to an end. However, the difficulty is yet to come: First of all, this is not a stand-alone game, it is a multiplayer real-time synchronization game. That means that you need to synchronize the drawn content to all players in real time, so the question is, how do you transmit in real time for a 960*640 canvas?

(1) Http protocol to pull the latest pictures

    First of all, we can think of using a patch-like method, upload the content to the resource library in png format every time the drawing is successful, and then other guessing players use the http protocol to pull the latest resources

    The advantage of this approach is to ensure the accuracy and completeness of the drawn content

   The downside is that every time the drawing content is updated, the resources must be pulled once. We know that the IO of the file is very time-consuming , so when the performance is a bit stuck, the update will flicker, so Player experience is not good

(2) RPC protocol to synchronize pixel content

    So can we synchronize the contents of the pixels? Although each pixel of the project is recorded in RGBA8888 format , that is to say, a pixel needs 32 bits, but we actually only use 8 colors, and the transparency is 255, so we can use 8 bits to The map records information about each pixel

 

    But even though it is so optimized, each pixel also needs to be recorded with a size of 1B, and we have 960*640 pixels, 960*640/1024, so we still need 600KB of data to transmit this picture.

    However, due to the monitoring of performance pressure, the server has been idle for a long time. Every time the RPC protocol is used, data exceeding 100KB cannot be exchanged, because if we need to use this method, we have to further optimize: we can divide the canvas into Blocks, cut into small pieces like a scene map to transmit separately , generally speaking, it is also a kind of thinking

    The advantage of this approach is to ensure the accuracy of the screen and save IO time

    The disadvantage is that it is necessary to request the content of the screen in blocks, and it is difficult to ensure that each small piece of content can be accurately obtained and displayed every time the data is transferred.

 

(3) RPC protocol synchronous drawing process

    In fact, the second idea is very close to the correct solution, but the only problem is that I don’t want to use the block method, and I want to synchronize a complete picture to the player. So I thought of the third method: record the player's drawing process, and use the RPC protocol to synchronize the player's drawing records.

    What does that mean? First, we divide the entire canvas into a whole composed of 3*3 or 4*4 pixel squares. Then we use a two-dimensional array to record whether these small squares are drawn, and what color mapping is drawn.

    Whenever the player draws, we update the mapping value of the small square in the two-dimensional array, and then splice and upload the record information of the two-dimensional array in the form of binary data. Players participating at the same time obtain the latest drawing information through the RPC protocol, then decode it into a two-dimensional array, and re-draw the content according to the drawing information

    In this way, we can combine the 3*3 or 4*4 pixel information into one copy. Although there will be some jaggedness and more time spent on encoding and decoding, we can successfully compress the data to less than 100K and avoid IO cards. pause

local NORMAL_LENGTH = 3

local i  = math.floor(y / NORMAL_LENGTH)
local j  = math.floor(x / NORMAL_LENGTH)
self.nodeList[i*NORMAL_LENGTH][j*NORMAL_LENGTH] = self:getEncodeNodeIndex()

(3) Finally, look at our final effect

You draw me guess how to play video

     So far, the sharing of how you draw and guess how to play is over~~

      Thanks for reading, like, follow! ! !

Guess you like

Origin blog.csdn.net/cooclc/article/details/131889890