Moderate

I’m trying to recreate the big guy animation

Influenced by the animation in Uncle Ran’s article, I wrote a small tool on a whim, which can quickly design the entire animation through programming. This time, I want to use this tool to restore the animation of The Handsome Ape.

Animation renderings of the big guy

Analysis of the

Through the analysis of the effect drawing to restore, it is preliminarily concluded that four functions need to be realized:

  1. Line drawing: Draw straight lines or dotted lines.
  2. The code is typed letter by letter and has a cursor.
  3. Text gradient display.

So take them one by one.

Break “Draw lines – Draw lines or dots”

Look at the

Through the renderings to understand the need to specify the character through the line frame. So let’s design a function that passes in the specified node and wireframes it. So let’s design a function: draw a line, draw a dot line and get the drawing point information.

To achieve “draw a straight line” :

How to design the function comfortably?

I want to be able to draw a line as soon as I pass in a starting point and an ending point, and I want to be able to pass in a progress value to control the progress of the drawing, to fast-forward it, to roll it back, and finally to pass in some information to adjust the drawing, like color, line width. Expectations are set, and develop accordingly.

The code is as follows:

drawLine({ ctx, start, end, progress, space, isAdd, strokeColor,lineWidth}) {
        ctx.strokeColor = strokeColor;
        ctx.lineWidth = lineWidth;
        let unit = space;
        let vline = end.sub(start);
        let vlineLen = vline.mag();
        let sumPart = parseInt(vlineLen / unit)
        let drawCount = parseInt(sumPart * progress);

        for (let i = 1; i < drawCount; i++) {
            let c = vline.mul(i / sumPart)
            ctx.lineTo(c.x + start.x, c.y + start.y);
        }
        if (progress == 1 && isAdd) {
            ctx.lineTo(end.x, end.y);
        }
        ctx.stroke();
    }
Copy the code

Parameters in

  • CTX: An instance of the draw class through which to draw.
  • The starting point start:
  • End: end
  • Progress: progress
  • Space: draws the spacing
  • IsAdd: Whether to add a drawing at the end
  • StrokeColor: Paints the color of the line

Achieve “draw dots and lines” :

According to the renderings, it is necessary to draw a line composed of dots. First, it is roughly the same as drawing a straight line, except for some parameters, such as the radius of the dots.

How to design the function comfortably?

Just like drawing a line, I want to be able to draw a line by passing in a starting point and an ending point, and I want to be able to pass in a progress value to control the progress of the drawing, to fast forward and roll back, and finally to pass in some information to adjust the drawing, such as the color of the drawing point and the size of the radius. Expectations are set, and develop accordingly.

The code is as follows:

drawPointLine({ ctx, start, end, progress, space, radius, isAdd,fillColor }) {
        let unit = radius * 2 + space;
        let vline = end.sub(start);
        let vlineLen = vline.mag();
        let sumPart = parseInt(vlineLen / unit)
        let drawCount = parseInt(sumPart * progress);
        for (let i = 1; i < drawCount; i++) {
            let c = vline.mul(i / sumPart)
            ctx.circle(c.x + start.x, c.y + start.y, radius);
            ctx.fillColor =fillColor
            ctx.fill();
        }
        if (progress == 1&& isAdd) { ctx.circle(end.x, end.y, radius); ctx.fillColor =fillColor ctx.fill(); }}Copy the code

Parameters in

  • CTX: An instance of the draw class through which to draw.
  • The starting point start:
  • End: end
  • Progress: progress
  • Space: draws the spacing
  • Radius: The radius required to draw a point
  • IsAdd: Whether to add a drawing at the end
  • FillColor: The color of the drawing point

To achieve “get drawing point information” :

How to design the function comfortably?

What I expect is that if I pass in a node of character type, and I pass in the subscript of the specified character, the function will give me information about the set of points that are drawn around that character, and I can also tweak it by passing in and out parameters, such as making the grid wider and moving it left and right. Expectations are set, and develop accordingly.

getPointFromCodeNode({node, letterIndex,unit,customOffsetX=0}) {
        let condeStr = node.getComponent(cc.RichText)
        let codeLen = condeStr.string.length
        let width = node.width;
        let height = node.height;
        let nodeY = node.y;
        let nodeX = node.x;
        let unitX = unit||width / codeLen
        let unitY = height / 2;
        let pointArr = [];
        let centerPoint = {
            x: nodeX + letterIndex * unitX - unitX / 2.y: nodeY
        }
        let offsetCommon = {
            oX: 5.oY: -5
        }
        let offsetArr = [
            { oX: -unitX / 2.oY: -unitY },
            { oX: -unitX / 2.oY: unitY },
            { oX: unitX / 2.oY: unitY },
            { oX: unitX / 2.oY: -unitY },
        ]
        for (let i = 0; i < 4; i++) {
            let offset = offsetArr[i];
            let pointTemp = cc.v2(centerPoint.x + offset.oX + offsetCommon.oX+customOffsetX, centerPoint.y + offset.oY + offsetCommon.oY);
            pointArr.push(pointTemp)
        }
        return pointArr
    }
Copy the code

It’s finished

Now that we have drawn straight lines and dotted lines, it is possible to draw wave lines, curves, and… All kinds of lines, not on the top to write a function for unified management, the code is not very chaotic.

Then develop a unified processing function handleDrawLine and function is to the unified processing incoming information, integrated management of multiple drawing basis functions (picturesque line, painting line), the various flexible deployment, the map link to the external exposure interface as much as possible, not to reveal, use you just need to focus on the main key parameters, Such as starting point information, drawing line progress.

The code is as follows:

handleDrawLine(data) {
        const { ctx, pointArr, progress ,drawType=DRAW_TYPE.TYPE_DEFAULT,isLoop=true} = data;
        ctx.clear();
        // Based on the set of points, get the set of vectors
        let vlineArr = [];
        let pointArrLen = pointArr.length;
        pointArr.forEach((item, index) = > {
            let start = item;
            let end;
            if (index === pointArrLen - 1) {
                end = pointArr[0];
            } else {
                end = pointArr[index + 1]
            }
            vlineArr.push(end.sub(start))
        });
        // If it is not a loop, remove the line that automatically closes last.
        if(! isLoop){ vlineArr = vlineArr.slice(0,vlineArr.length-1);
        }
        // Get the set of all vector lengths
        let vlineMagArr = vlineArr.map((item) = > {
            return item.mag()
        });
        // Get the total length
        let totalMag = vlineMagArr.reduce((total, current) = > {
            return total + current
        }, 0)
        let basePoint = [pointArr[0]].let targetLength = 0;
        vlineMagArr.forEach((itemMag, index) = > {
            targetLength += itemMag;
            if (targetLength / totalMag < progress) {
                basePoint.push(pointArr[index + 1])}})let processBaseLine = (params) = >{
            const {type,point,index} = params;
            if (type === DRAW_TYPE.TYPE_CIRCLE) {
                this.drawPointLine({ ... data,start: basePoint[index - 1].end: point, progress: 1.isAdd: true})}else if (drawType === DRAW_TYPE.TYPE_DEFAULT) {
                ctx.lineTo(point.x, point.y)
            }
        }
        let processDrawLine = (params) = >{
            const {type} = params;
            if (type === DRAW_TYPE.TYPE_CIRCLE) {
                this.drawPointLine({ ... data, ... targetPoint,progress: progressTemp });
            } else if (drawType === DRAW_TYPE.TYPE_DEFAULT) {
                this.drawLine({ ... data, ... targetPoint,progress: progressTemp }); }}// Draw fixed lines according to the basic points
        basePoint.forEach((point, index) = > {
            if (index === 0) {
                ctx.moveTo(point.x, point.y);
            } else {
                processBaseLine({type:drawType,point,index})
            }
        })
        // Draw dynamic lines based on target points
        let targetPointIndex = basePoint.length;

        let targetPoint;
        if (targetPointIndex < pointArr.length) {
            targetPoint = {
                start: pointArr[targetPointIndex - 1].end: pointArr[targetPointIndex]
            }
        } else {
            targetPoint = {
                start: pointArr[targetPointIndex - 1].end: pointArr[0]}}let progressTemp = (progress * totalMag - vlineMagArr.slice(0, targetPointIndex - 1).reduce((total, current) = > {
            return total + current
        }, 0)) / vlineMagArr[targetPointIndex - 1]; processDrawLine({ ... data, ... targetPoint,progress: progressTemp,type:drawType })
    }
Copy the code

Achieved effect

Break the problem spot “code typed letter by letter with cursor”

This problem is much easier to solve, if you write a separate code to type in the function, it may take some effort, but remember, the shelf I wrote the entity to implement the life cycle function with the reaction of the action of the executive function, to solve this problem, it is absolutely unnecessary.

Just find an individual entity, and configure its action list, specify the operation life cycle of the action, and in the action according to the progress to control the code typing, control the cursor node position, then the rest of the shelf is OK.

The code is as follows:

this.recationArr = [
  {
    start: this.customStart,
    end: this.customStart + 0.1.action: (data = {}) = > {
      const { progress } = data;
      let richText = self.getComponent(cc.RichText);
      let endId = parseInt(progress * this.codeArr.length);
      let tempArr = this.codeArr.slice(0, endId);
      self.curr.setContentSize(
        cc.size(6, richText.node.getContentSize().height * 0.7));// This section is very low... I want to be elegant, but I feel like parsing code and configuring colors is a big feature to write. For the time being, I'm going to match colors based on strings, because there aren't many characters.
      tempArr = tempArr.map((item) = > {
        if (self.code.includes("methods")) {
          if ("methods".indexOf(item) > -1) {
            return (item = `<color=#1e88df>${item}</color>`); }}if (self.code.includes("return")) {
          if ("return".indexOf(item) > -1) {
            return (item = `<color=#AD582E>${item}</color>`); }}if (self.code.includes("data")) {
          if ("data".indexOf(item) > -1) {
            return (item = `<color=#5ab23c>${item}</color>`); }}return item;
      });
      richText.string = tempArr.join("");
      self.curr.x = self.node.x + richText.node.width;
      self.curr.y = richText.node.y - 2; }},];Copy the code

Remember that the cursor has to blink

Use a combination of native lifecycle Updates and entity lifecycle processes.

update(){
        this._super();
        if(this.isStop){
            this.curr.active = false
        }else{
            if(!this.shakeFlag){
                this.shakeFlag = Date.now()
            }else{
                let now = Date.now()
                if((now - this.shakeFlag)>500) {this.shakeFlag = now;
                    this.curr.active = !this.curr.active; }}}},process(props){
        if(this._super(props)===0) {return
        }
        if(this.isStop){
            this.curr.active = false
        }else{
            this.shakeFlag = 0;
            this.curr.active = true; }}Copy the code

Achieved effect

  • this.isStop: Indicates whether to stop blinking
  • this.shakeFlag: Controls flashing variables

Break: Text gradient display…

Em ~ ~ ~, this, how can I say, I just realized the function, but you asked me to tell you something, I really do not have that skill, the use of shader, I believe that the students who have studied WebGL must be familiar with, when I was developing Cesium, I read half of webGL books, that is to say, I have a superficial understanding. Can barely through baidu oriented programming, effectively collect information to complete the function, so that the function implementation, but this is a very worth probing deep field, I am very interested in, but then again, my attitude for knowledge is the truth, just enough, blunt is “neutral and engraved”, as the present situation, to deliberate practice, When I have the opportunity or need in the future, I may study in depth and systematically.

Achieved effect

Full rendering

conclusion

At present, the tool is still improving, and there is still a long way to go. I will continue to polish it in the future, and make a trial version as soon as possible. If there are seniors and classmates who need me to restore, please tell me, AND I will try my best to arrange it (if I can’t arrange it, I may just have no time to do it, haha).