[HTML5 Game Development] challenge horizontal Act (III)

Source: Internet
Author: User

This is the third article in this series. For other articles, see the directory in the following post.

Http://blog.csdn.net/lufy_legend/article/details/8441677

Preface

This series of tutorials is the first time I have used the adjustable-style development. Most of the content in this article is drool, so you can simply read it empty and guard against flooding.

The first bottleneck for this type of development is material. Thanks to the user yorhomwang for providing a large number of material URLs.

The following is the result of this development. Let's preview it first.

Material Conversion Tool

All the materials found this time are GIF images. to use them in the game, you need to extract each frame of GIF images to form a new image, I used flash to create a simple conversion tool. If you need it, please use it directly. The URL is as follows.

Http://lufylegend.com/flash/demo/GifToPng

Question

It can be said that General Huang has been cool by me on the battlefield for some time. These days have become boring and have gone crazy with a knife on the battlefield, roaring ......

I couldn't bear to watch it anymore. I decided to continue writing a few lines of code to give him some fun.

I. Battlefield mobile

First, let's move the map on the battlefield first. You can't always run it all in one place and define a variable.

var back_run = true;

When this variable is true, the battlefield background map can be moved; otherwise, it cannot be moved.

Then add the code to control the battlefield movement in the move function of player. js. Why is it in player. js instead of character. js? Because maps are moved only when the hero, General Huang, moves. character is the parent class, and the enemy will inherit from this class.

Player.prototype.move = function (){var self = this, mx = 0, my = 0;if(keyCtrl[KEY.LEFT] && charaLayer.x + self.x > 0){mx = -1;}else if(keyCtrl[KEY.RIGHT] && charaLayer.x + self.x < LGlobal.width){mx = 1;}if(keyCtrl[KEY.UP]){my = -1;}else if(keyCtrl[KEY.DOWN]){my = 1;}self.mx = mx;self.my = my;if(self.action == ACTION.RUN){mx *= 2;my *= 2;}else if(self.action == ACTION.HIT){mx = 2*(self.direction == DIRECTION.RIGHT ? 1 : -1);my = 0;}if(back_run && mx > 0 && charaLayer.x + self.x > LGlobal.width * 0.5){var setX = mx*MOVE_STEP;if(backLayer.data.x + setX + backLayer.data.width > backLayer.data.image.width){back_run = false;setX = backLayer.data.image.width - backLayer.data.width - backLayer.data.x;}charaLayer.x -= setX;backLayer.data.setCoordinate(backLayer.data.x + setX,backLayer.data.y);addEnemy();}self.callParent("move",arguments);};

As you can see, when the hero moves, the map coordinates will be increased or decreased in the opposite direction, which enables visual map movement. Note that one line calls the addenemy function, this function detects whether to add an enemy to the battlefield.

2. Increase the number of enemies

Next let's take a look at how to add an enemy. I found several materials from Sun shangxiang, which are comprehensive. here is one of them, which is not listed in detail.

Add an enemy. js class below

function Enemy(list,speed){var self = this;base(this,Character,[list,speed]);self.belong = "enemy";self.hp = 100;};Enemy.prototype.onjump = function (){var self = this;self.callParent("onjump",arguments);self.setLocation();var index = self.anime.colIndex;self.yArr = [0,-10,-20,-30,-40,-40,-30,-20,-10,0];self.anime.y += self.yArr[index];};Enemy.prototype.onjump_attack = function (){var self = this;self.callParent("onjump_attack",arguments);self.setLocation();var index = self.anime.colIndex;if(index >= self.yArr.length)return;self.anime.y += self.yArr[index];};Enemy.prototype.setAction = function (action,direction){var self = this,yArr = new Array();if(action == ACTION.MOVE && self.action == ACTION.JUMP)return;if(action == ACTION.JUMP_ATTACK){var index = self.anime.colIndex,i;for(i = index;i
 
   0){self.anime.y += self.yArr[0];}};Enemy.prototype.overActionRun = function (lastAction,animeAction){var self = this;self.callParent("overActionRun",arguments);keylock = false;if(lastAction == ACTION.FALL){if(self.direction == DIRECTION.LEFT){self.x += 80;}else{self.x -= 80;}}};Enemy.prototype.move = function (){var self = this, mx = 0, my = 0;self.mx = mx;self.my = my;self.callParent("move",arguments);};
 

This class and player. JS is similar. It inherits from the character class, but the constructor does not pass in every related array. It is too troublesome. Instead, it uploads an array list, it contains all the parameters to be set.

When the addenemy () function for adding enemies is called, we cannot add enemies without limit. Therefore, we are prepared in advance to add enemies and when to add them.

var enemy_list = new Array({name:"sunji",x:800,y:350,when_x:300,back_run:false},{name:"huangzhong",x:1200,y:280,when_x:800,back_run:true});function addEnemy(){if(enemy_list.length == 0)return;if(enemy_list[0].when_x > hero.x)return;var charadata = CharacterList[enemy_list[0].name]();var enemy = new Enemy(charadata);enemy.x = enemy_list[0].x;enemy.y = enemy_list[0].y;charaLayer.addChild(enemy);enemy_list.shift();}

I added an enemy_list array, which contains information such as when to add the enemy's when_x, the coordinates of the enemy's position, and whether the map stops moving when the character appears, when the addenemy function is called, the position of when_x and the hero is used to determine whether to add the enemy.

Iii. Attacks

If you have an enemy, you have to fight. Let's talk about the attack and the determination of the attack.

Attack determination is actually a collision detection. Of course we can use pixel-level collision, but for such games, it is a little tricky, and the efficiency is too low, therefore, rectangular collision detection is used here.

Lufylegend. the JS engine contains lglobal. the hittest () function can be used to detect whether a rectangle is collided. However, because there are many blank areas in the image material and this method is directly used, the error is too large, therefore, I set the attack range and attack scope to be detected for each frame of the character in advance. See the characterlist below. JS Code.

VaR characterlist = {Huangzhong: function () {// var datalist = new array (); datalist. push (New lbitmapdata (imglist ["player_stand"],); datalist. push (New lbitmapdata (imglist ["player_move"], 115,85); datalist. push (New lbitmapdata (imglist ["player_run"],); datalist. push (New lbitmapdata (imglist ["player_jump"], 131,134,); datalist. push (New lbitmapdata (imglist ["player_attack"], 0, 0, 242,143); datalist. push (New lbitmapdata (imglist ["player_big_attack"], 232,143,); datalist. push (New lbitmapdata (imglist ["player_jump_attack"], 232,143,); datalist. push (New lbitmapdata (imglist ["player_hit"],); datalist. push (New lbitmapdata (imglist ["player_skill"], 324,140,); datalist. push (New lbitmapdata (imglist ["player_big_skill"], 441,166,); datalist. push (New lbitmapdata (imglist ["player_hert"], 179,87); datalist. push (New lbitmapdata (imglist ["player_fall"], 298,157,); // var coordinatelist = new array (); coordinatelist. push (lglobal. dividecoordinate (,); coordinatelist. push (lglobal. dividecoordinate (, 85,); coordinatelist. push (lglobal. dividecoordinate (, 87,); var jumplist = lglobal. dividecoordinate (655,134,); coordinatelist. push ([[jumplist [0] [0], jumplist [0] [0], jumplist [0] [1], jumplist [0] [1], jumplist [0] [2], jumplist [0] [2], jumplist [0] [3], jumplist [0] [3], jumplist [0] [4], jumplist [0] [4]); var attacklist = lglobal. dividecoordinate (484,143,); coordinatelist. push ([[attacklist [0] [0], attacklist [0] [1], attacklist [0] [1], attacklist [0] [1]); vaR bigattacklist = lglobal. dividecoordinate (927,143,); coordinatelist. push (bigattacklist); var jumpattacklist = lglobal. dividecoordinate (927,143,); coordinatelist. push (jumpattacklist); coordinatelist. push (lglobal. dividecoordinate (966,88,); coordinatelist. push (lglobal. dividecoordinate (2268,140,); var bigskilllist = lglobal. dividecoordinate (2205,830,); coordinatelist. push ([[bigskilllist [0] [0], bigskilllist [0] [1], bigskilllist [0] [2], bigskilllist [0] [3], bigskilllist [0] [4], bigskilllist [1] [0], bigskilllist [1] [1], bigskilllist [1] [2], bigskilllist [1] [3], bigskilllist [1] [4], bigskilllist [2] [0], bigskilllist [2] [1], bigskilllist [2] [2], bigskilllist [2] [3], bigskilllist [2] [4], bigskilllist [3] [0], bigskilllist [3] [1], bigskilllist [3] [2], bigskilllist [3] [3], bigskilllist [3] [4], bigskilllist [4] [0], bigskilllist [4] [1], bigskilllist [4] [2], bigskilllist [4] [3], bigskilllist [4] [4]); var hertlist = lglobal. dividecoordinate (358,87,); coordinatelist. push ([[hertlist [0] [0], hertlist [0] [0], hertlist [0] [1], hertlist [0] [1]); vaR falllist = lglobal. dividecoordinate (2682,157,); coordinatelist. push ([[falllist [0] [0], falllist [0] [1], falllist [0] [2], falllist [0] [3], falllist [0] [4], falllist [0] [5], falllist [0] [6], falllist [0] [6], falllist [0] [6], falllist [0] [7], falllist [0] [7], falllist [0] [6], falllist [0] [6], falllist [0] [7], falllist [0] [8]); // var locationlist = new array (); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 20, Y: 20}); locationlist. push ({X: 20, Y: 20}); locationlist. push ({X: 20, Y: 20}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 100, Y: 0}); locationlist. push ({X: 150, Y: 20}); locationlist. push ({X: 5, Y: 0}); locationlist. push ({X:-30, Y: 10}); // attacked range: var hertlist = [[-30,-60, 60, 50], [-30, -60, 60, 50], [-30,-60, 60, 50], [-30,-60, 60, 50], [-30,-60, 60, 50], [-30, -60, 60, 50], [-30,-60, 60, 50], [-30,-60, 60, 50], [-30,-60, 60, 50], [-30, -60, 60, 50], [-30,-60, 60, 50], [-30,-60, 60, 50], [[-30,-70, 50, 60], [-30,-70, 50, 60], [-30,-70, 50, 60], [-30,-70, 50, 60], [-30,-70, 50, 60], [-30,-70, 50, 60], [-30,-70, 50, 60], [-30,-70, 50, 60], [[-30,-70, 60, 60], [-30,-70, 60], [-30,-70, 60, 60], [-30,-70, 60], [-30,-70, 60, 60], [-30,-70, 60], [[-25,-70, 50, 60], [-25,-70, 50, 60], [-25, -70, 50, 60], [-25,-70, 50, 60], [-25,-70, 50, 60], [[-10,-60, 30, 60], [-10,-60,30, 60], [-30,-60,30, 60], [-30,-60,30, 60], [[0,-60,40, 60], [0,-60, 40, 60], [-20,-60, 30, 60], [-20,-60, 30], [], [[-20,-60, 30, 60], [-20,-60, 30, 60], [-20,-60, 30], [-20,-60, 30, 60], [-20,-60, 30], [-20,-60, 30, 60], [[0,-70, 40], [0,-70, 40, 60], [], [], []; // attack range var attacklist = [[], [], [], [], [], [0, 0, 0, 0], [0, 0, 0], [-10,-70,115, 60], [-10,-70,115, 60], [[0, 0, 0], [0, 0, 0, 0, 0], [-10,-100,140, 90], [-10,-100,140, 90], [[,], [,], [-10,-130,115, 60], [-10,-110,140,120], [[10,-70, 30], [10,-70, 30, 70], [10,-70,30, 70], [10,-70,30, 70], [10,-70,30, 70], [10,-70,30, 70], [[100,130,100,], [,], [-40,-70, 80, 60], [-60,-, 80, 60], [20,-], [20,-100,130,100], [[0, 0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0, 0], [100,120,], [,], [,], [,], [50,-, 20], [60,-, 40], [60, -90,150, 40], [50,-80,190, 40], [50,-80,210, 40], [50,-75,310, 60], [50,-75,310, 60], [50,-75,310, 60], [50,-75,310, 80], [], []; return [datalist, coordinatelist, locationlist, hertlist, attacklist] ;}, SUNJI: function () {// var datalist = new array (); datalist. push (New lbitmapdata (imglist ["sunji_stand"],); datalist. push (New lbitmapdata (imglist ["sunji_move"],); datalist. push (New lbitmapdata (imglist ["sunji_run"],); datalist. push (New lbitmapdata (imglist ["sunji_jump"], 131,134,); datalist. push (New lbitmapdata (imglist ["sunji_attack"], 0, 0, 197,103); datalist. push (New lbitmapdata (imglist ["sunji_big_attack"], 198,103,); datalist. push (New lbitmapdata (imglist ["sunji_jump_attack"], 182,143,); datalist. push (New lbitmapdata (imglist ["sunji_hit"],); datalist. push (New lbitmapdata (imglist ["sunji_skill"], 215,102,); datalist. push (New lbitmapdata (imglist ["sunji_big_skill"], 275,139,); datalist. push (New lbitmapdata (imglist ["sunji_hert"],); datalist. push (New lbitmapdata (imglist ["sunji_fall"], 249,136,); // var coordinatelist = new array (); coordinatelist. push (lglobal. dividecoordinate (,); coordinatelist. push (lglobal. dividecoordinate (, 97,); coordinatelist. push (lglobal. dividecoordinate (, 77,); var jumplist = lglobal. dividecoordinate (655,134,); coordinatelist. push ([[jumplist [0] [0], jumplist [0] [0], jumplist [0] [1], jumplist [0] [1], jumplist [0] [2], jumplist [0] [2], jumplist [0] [3], jumplist [0] [3], jumplist [0] [4], jumplist [0] [4]); var attacklist = lglobal. dividecoordinate (394,103,); coordinatelist. push ([[attacklist [0] [0], attacklist [0] [1], attacklist [0] [1], attacklist [0] [1]); vaR bigattacklist = lglobal. dividecoordinate (792,103,); coordinatelist. push (bigattacklist); var jumpattacklist = lglobal. dividecoordinate (728,143,); coordinatelist. push (jumpattacklist); coordinatelist. push (lglobal. dividecoordinate (1428,86,); coordinatelist. push (lglobal. dividecoordinate (2365,102,); var bigskilllist = lglobal. dividecoordinate (1650,695,); coordinatelist. push ([[bigskilllist [0] [0], bigskilllist [0] [1], bigskilllist [0] [2], bigskilllist [0] [3], bigskilllist [0] [4], bigskilllist [0] [5], bigskilllist [1] [0], bigskilllist [1] [1], bigskilllist [1] [2], bigskilllist [1] [3], bigskilllist [1] [4], bigskilllist [1] [5], bigskilllist [2] [0], bigskilllist [2] [1], bigskilllist [2] [2], bigskilllist [2] [3], bigskilllist [2] [4], bigskilllist [2] [5], bigskilllist [3] [0], bigskilllist [3] [1], bigskilllist [3] [2], bigskilllist [3] [3], bigskilllist [3] [4], bigskilllist [3] [5], bigskilllist [4] [0], bigskilllist [4] [1], bigskilllist [4] [2], bigskilllist [4] [3], bigskilllist [4] [4], bigskilllist [4] [5]); var hertlist = lglobal. dividecoordinate (262,79, 1,2); coordinatelist. push ([[hertlist [0] [0], hertlist [0] [0], hertlist [0] [1], hertlist [0] [1]); vaR falllist = lglobal. dividecoordinate (1245,544,); coordinatelist. push ([[falllist [0] [0], falllist [0] [1], falllist [0] [2], falllist [0] [3], falllist [0] [4], falllist [1] [0], falllist [1] [1], falllist [1] [2], falllist [1] [3], falllist [1] [4], falllist [2] [0], falllist [2] [1], falllist [2] [2], falllist [2] [3], falllist [2] [4], falllist [3] [0], falllist [3] [1], falllist [3] [2], falllist [3] [3], falllist [3] [4]); // var locationlist = new array (); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 40, Y: 8}); locationlist. push ({X: 20, Y: 0}); locationlist. push ({X: 20, Y: 20}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 0, Y: 0}); locationlist. push ({X: 70, Y: 10}); locationlist. push ({X: 5, Y: 0}); locationlist. push ({X:-35, Y: 0}); // attacked range: var hertlist = [[-25,-70, 60], [-25, -70, 60, 60], [-25,-70, 60], [-25,-70, 60], [-25,-70, 60], [-25,-70, 60], [-25, -70, 60, 60], [-25,-70, 60], [-25,-70, 60], [-25,-70, 60], [-25,-70, 60], [-25, -70, 60, 60], [-25,-70, 60], [-25,-70, 60], [[-25,-90, 50, 80], [-25,-90,50, 80], [-25,-90,50, 80], [-25,-90,50, 80], [-25,-90,50, 80], [-25,-90,50, 80], [-25,-90,50, 80], [-25,-90,50, 80], [[-30,-60, 70, 40], [-30,-60, 70, 40], [-30,-60, 70, 40], [-30,-60, 70, 40], [-30,-60, 70, 40], [-30,-60, 70, 40], [[-25,-90,50, 70], [-25,-90,50, 70], [-25, -90,50, 70], [-25,-90,50, 70], [-25,-90,50, 70], [[-20,-80, 50, 70], [-20,-80, 50, 70], [-10,-60, 70, 50], [-10,-60, 70, 50], [[-10,-80, 50, 60], [-10,-80, 50, 60], [-10,-80, 50, 60], [-10,-80, 50, 60], [-30, -80, 50, 70], [-30,-80, 50, 70], [-30,-80, 50, 70], [-30,-80, 50, 70], [[-20,-70, 60, 60], [-20,-70, 60], [-20,-70, 60], [-20,-70, 60, 60], [-20,-70, 60, 60], [-20,-70, 60], [[-10,-80, 40, 70], [-10, -80, 40, 70], [], [], []; // attack range var attacklist = [[], [], [], [], [], [[0, 0, 0], [0, 0, 0], [30,-70, 75, 60], [30,-70, 75, 60], [[0, 0, 0, 0], [,], [20,-, 80, 90], [20,-, 80], [[,], [,], [-10,-90,100, 80], [-10,-90,100, 80], [[10,-70, 50], [10,-70, 50, 70], [10,-70, 50, 70], [10,-70, 50], [10,-70, 50, 70], [10,-70, 50, 70], [[70,130, 100], [80,140,], [-30,-70, 90, 60], [-90,-, 60, 70], [-40,-80,140, 70], [[,], [,], [,], [,], [100,100, 110,100], [0,-110,100, 40], [0,-, 50], [0,-, 50], [,], [20,-120,140,120], [20,-120,130,120], [-50,-120,160,120], [-60,-80,180, 80], [-20,-50,150, 60], [-10,-60,150, 60], [50,-60, 90, 60], [50,-75,150, 70], [50,-75,150, 70], [50,-75,150, 70], [50,-75,150, 70], [], []; return [datalist, coordinatelist, locationlist, hertlist, attacklist] ;}}

In this way, we can directly detect collision through these preset ranges. How can we detect such rectangular collision? Of course, lufylegend is used. A rectangle class lrectangle In the JS engine. For detailed usage, refer to the official API documentation. The collision detection function used this time is the intersects () function. This function is used to detect whether two lrectangle objects overlap, whether or not a collision occurs.

Because both parties and places can be attacked, the attack detection loads the character of the parent class and adds the following code to the onframe function of the character class.

if(self.action == ACTION.ATTACK || self.action == ACTION.BIG_ATTACK || self.action == ACTION.HIT || self.action == ACTION.JUMP_ATTACK || self.action == ACTION.SKILL || self.action == ACTION.BIG_SKILL){for(key in charaLayer.childList){chara = charaLayer.childList[key];if(self.belong == chara.belong)continue;self.checkAction(chara);}}

That is to say, when the current character's action is in an attack or other action and is an enemy, it enters the attack detection checkaction function. The checkaction function is as follows.

Character.prototype.checkAction = function (chara){var self = this;var attack_rect = self.getAttackRect();var hert_rect = chara.getHertRect();if(!attack_rect || !hert_rect)return;if(attack_rect.intersects(hert_rect) && Math.abs(self.y - chara.y) < 30){if(self.action == ACTION.ATTACK){chara.setAction(ACTION.HERT,chara.direction);}else{var dir = DIRECTION.RIGHT;if(self.x < chara.x)dir = DIRECTION.LEFT;chara.setAction(ACTION.FALL,dir);}}}

The getattackrect function and the gethertrect function return the current attack and the range of the attacked lrectangle object respectively, and then use the intersects function to determine whether the target object has been attacked, the attacked party changes to the attacked State, and other attack methods change to the falling state.

The getattackrect and gethertrect functions are as follows.

Character.prototype.getAttackRect = function(){var self = this;attackList = self.attackList[self.action];if(self.anime.colIndex >= attackList.length)return false;var rect = attackList[self.anime.colIndex];var x = rect[0],y=rect[1],w=rect[2],h=rect[3];if(x == 0 && y == 0 && w == 0 && h == 0)return false;y += self.y;if(self.direction == DIRECTION.LEFT){x = self.x - x - w;}else{x = self.x +x;}return new LRectangle(x,y,w,h);}Character.prototype.getHertRect = function(){var self = this;var hertList = self.hertList[self.action];if(self.anime.colIndex >= hertList.length)return false;var rect = hertList[self.anime.colIndex];var x = rect[0],y=rect[1],w=rect[2],h=rect[3];if(x == 0 && y == 0 && w == 0 && h == 0)return false;y += self.y;if(self.direction == DIRECTION.LEFT){x = self.x - x - w;}else{x = self.x +x;}return new LRectangle(x,y,w,h);}

Okay, the AI and sound effects of the remaining enemies. Let's continue later. Now, you can click the test connection below to see the results.

Http://lufy.netne.net/lufylegend-js/act03/index.html

General Huang was excited when he heard that sun shangxiang was coming soon. It was a beautiful one, and his eyes were squashed into a heart-shaped shape. He shouted, Shang Xiang, And he waited for hundreds of years. This was a big surprise to me. Isn't Sun shangxiang Liu Bei's wife? It's the old guy's Master. It's just so bad. But the old general shouted excitedly, "rely on! Will I let you know that I have been in love with her for centuries? The old man will never admit it .", "If I was a young man for decades, why would I think so? After all, she is the wife of the master .", "No, the master is also old. Ah, old man is a loyal minister. I will not compete with the master .", "The old man did not say anything. Lufy, let me know, old man said nothing .". I hurriedly said, "Ah, I didn't hear anything ......"

However, what is the purpose? When the old guy starts playing, he starts to run forward. while running, he mutates, "Girl, touch the Lord ......". For some reason, 1000 words are omitted here.

Finally, Sun shangxiang appeared, and old Huang Zhong suddenly rushed over. Just reached out and touched it, and found Sun shangxiang was beaten by him and ran around, and immediately shouted, "lufy, what the old man wants is touch, not cut ".

Lufy: "Hum, old-colored ghost, how can I help you, let you succeed ......", In addition, he still does not know that he has prepared another Huang Zhong and Sun shangxiang for him. Let's see how they rob him. Haha.

Alas, it is:
Huang Zhong, the secret of the artifact, will kill the enemy. The century-old treasure knife is still blank.

Source code

Now let's download the source code. If you like it, take a look.

Http://fsanguo.comoj.com/download.php? Iw.act03.rar

Note: This attachment only contains the source code of this Article. For the lufylegend. js engine, goHttp://lufylegend.com/lufylegend.

Reprinted Please note: transferred from lufy_legend's blog

Continue to follow my blog

Http://blog.csdn.net/lufy_legend

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.