Mql4 Meta trader4, I can not make the indicator shift left simultaneously with the candles when new bar is formed - mql4

Below is the current code.
The indicator compiles and runs
When running the waves move with the candles as new bar forms.
But when you turn the platform off for some time and then turn it back on The candles and sin waves are now no longer synchronized.
Somehow the subwindow rests back to when the platform was turned on.
So no one out of 92 looky-loos wants to tackle this problem. I tried to put 3 pictures on here to help explain, but the system here will not display the pictures. If you are a coder trying to help, copy the code here, and paste it into your platform. Run it. Mark a high peak or low trough with a text symbol that will not move and then put a vertical line through the text character and the candle above it. as the program proceeds the vertical line will move with the candles and the wave moves with the vertical line, the text marker stays put so you can see where you started. Then turn off your platform with the indicator still loaded, wait for several candle periods then turn the platform back on and you will see that the waves reset back to the right. the vertical line continued on with the appropriate candle and the text marker also reset to the right with the peak or valley of the sin wave.
//---- indicator settings
#property indicator_separate_window
#property indicator_buffers 8 // 7
#property indicator_color1 Red
#property indicator_color2 Orange
#property indicator_color3 Yellow
#property indicator_color4 Lime
#property indicator_color5 Blue
#property indicator_color6 DodgerBlue
#property indicator_color7 DarkViolet
#property indicator_color8 White
//---- indicator buffers
double ExtBuffer1[], ExtBuffer2[], ExtBuffer3[], ExtBuffer4[],
ExtBuffer5[],ExtBuffer6[], ExtBuffer7[], ExtBuffer8[];
double Dgr1[], Dgr2[], Dgr3[], Dgr4[], Dgr5[], Dgr6[], Dgr7[],
Dgr8[];
extern datetime StartTime=D'1999.11.10 00:00';
//+-------------------------------------------------------------+
//| Custom indicator initialization function |
//+-------------------------------------------------------------+
int init()
{
IndicatorBuffers(7); // 6
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtBuffer1);
SetIndexShift(0,295);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtBuffer2);
SetIndexShift(1,264);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtBuffer3);
SetIndexShift(2,203);
SetIndexStyle(3,DRAW_LINE); SetIndexBuffer(3,ExtBuffer4);
SetIndexShift(3,166);
SetIndexStyle(4,DRAW_LINE); SetIndexBuffer(4,ExtBuffer5);
SetIndexShift(4,74);
SetIndexStyle(5,DRAW_LINE); SetIndexBuffer(5,ExtBuffer6);
SetIndexShift(5,102); // DodgerBlue
SetIndexStyle(6,DRAW_LINE); SetIndexBuffer(6,ExtBuffer7);
SetIndexShift(6,78); // Dark Violet
SetIndexStyle(7,DRAW_LINE);
SetIndexBuffer(7,ExtBuffer8); SetIndexShift(7,85); // White
SetLevelValue(0,0);
SetIndexStyle(0,DRAW_LINE,0,3); // 0
SetIndexStyle(2,DRAW_LINE,0,3); // 0
SetIndexStyle(3,DRAW_LINE,0,3); // 0
SetIndexStyle(4,DRAW_LINE,0,3); // 0
SetIndexStyle(5,DRAW_LINE,0,3); // 0
SetIndexStyle(6,DRAW_LINE,0,3); // 0
SetIndexStyle(7,DRAW_LINE,0,3); // 0
return(0);
}
//+-------------------------------------------------------------+
//| Accelerator/Decelerator Oscillator |
//+-------------------------------------------------------------+
int start()
{
int limit; // -----------------------------mine
int counted_bars=IndicatorCounted();
//---- last counted bar will be recounted
if(counted_bars>0) counted_bars--;
limit=Bars-counted_bars;
//-----------------------------------------mine
int Shift;
int i;
int b; // mine
Shift=iBarShift(Symbol(),PERIOD_CURRENT,StartTime);
ArrayResize(Dgr1,Shift+1);
ArrayResize(Dgr2,Shift+1);
ArrayResize(Dgr3,Shift+1);
ArrayResize(Dgr4,Shift+1);
ArrayResize(Dgr5,Shift+1);
ArrayResize(Dgr6,Shift+1);
ArrayResize(Dgr7,Shift+1);
ArrayResize(Dgr8,Shift+1);
MyCalc(Shift,1);
for(b=Shift; b<limit; b++) // mine
{
for(i=Shift; i>=0; i--) // >
{
ExtBuffer1[i]=Dgr1[i];
ExtBuffer2[i]=Dgr2[i];
ExtBuffer3[i]=Dgr3[i];
ExtBuffer4[i]=Dgr4[i];
ExtBuffer5[i]=Dgr5[i];
ExtBuffer6[i]=Dgr6[i];
ExtBuffer7[i]=Dgr7[i];
ExtBuffer8[i]=Dgr8[i];
}
} // mine
return(0);
}
//+-------------------------------------------------------------+
void MyCalc(int Shift, int Yhigh )
{
int i;
for(i=Shift;i>=0;i--)
{
Dgr1[i]=i*2.5; Dgr2[i]=i*2.5; Dgr3[i]=i*2.5; Dgr4[i]=i*2.5;
Dgr5[i]=i*2.5;
double val1=i*2.5;
double val2=i*2.5;
double val3=i*2.5;
double val4=i*2.5;
double val5=i*2.5;
double val6=i*2.5;
double val7=i*2.5;
double val8=i*2.5;
Dgr1[i]=MathSin(3.14159*val1/298)- 1/2 * Yhigh;
Dgr2[i]=MathSin(3.14159*val2/149)- 1/2 * Yhigh;
Dgr3[i]=MathSin(3.14159*val3/98)- 1/2 * Yhigh;
Dgr4[i]=MathSin(3.14159*val4/75)- 1/2 * Yhigh;
Dgr5[i]=.5*MathSin(3.14159*val5/60)- 1/2 * Yhigh;
Dgr6[i]=.5*MathSin(3.14159*val6/48)- 1/2 * Yhigh;
Dgr7[i]=.5*MathSin(3.14159*val7/42)- 1/2 * Yhigh;
Dgr8[i]=.5*MathSin(3.14159*val8/38)- 1/2 * Yhigh;
}
}

Your problem is that the indicator starts drawing at D'1999.11.10 00:00', however as this date is no doubt not available in your visible chart history, the indicator instead starts redrawing as the earliest available bar. This continues as you run the indicator, however when restarting MQL4, the visible bars is limited by your settings (Tools>Options>Charts>Max bars in chart) and upon reloading the indicator again reverts to start drawing at the earliest available bar which has now changed. As stated in my original comment, your calculation are not directly linked to candles so they will not shift consistently. If you wish to make them consistently, make the start point consistent somehow.

Related

The multicurrencies indicator doesn't draw and works fine in mql4

I wrote a mql4 multi-currency indicator (later I'll do it in mql5) which plots 8 buffers, 1 for each currency (AUD, CAD, etc...). The resulting value is then passed with a double smoothing (like the TSI indicator), basically I'm trying to reproduce the FOTSI indicator (here the Tradingview code https://it.tradingview.com/script/xjLnpOze/).
The problems I encounter are:
if I load the indicator on two different charts (e.g. EURUSD and GBPAUD) it draws differently even though there is nothing in the code that recalls the symbol of the chart it runs on.
if I update the graph after n bars (right mouse button) the indicator changes values and redraws itself. It is not a repainting in bad faith but evidently recharges the new values.
if I try to do the backtest with visualization, the indicator goes bust, i.e. it draws badly and goes haywire... even without visualization, when the backtest ends and it plots itself on the graph, it is slightly different from the one on the demo.
In the MT4 platform I only have the H1 data (all other timeframes empty) for all 28 symbols and it is disconnected from the internet, each bar takes the closing values from the 7 charts involved (EUR from EURAUD, EURCAD, EURCHF, etc.. ...).
I also thought about the asynchronous receipt of ticks, i.e. when the tick arrives on the main chart (where the indicator runs) it may not have arrived on some of the other charts so incorrect values are created which are fixed upon updating, right? But I can't figure out if that's the problem...
Maybe it's better to run the calculations only when all the charts have changed the bar then take the close[1]?
Here part of the code (only for AUD), the formula function calculats value Close[i] - Open [i] and I have different formulas.
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
int i, total;
total = prev_calculated == 0 ? rates_total - 1 : rates_total - prev_calculated;
//---- AUD
for(i = total; i >= 0; i--)
{
mtmAUD[i] = formula(formulas, currencies[0], i);
mtmAbsAUD[i] = MathAbs(mtmAUD[i]);
smooth1AUD[i] = iMAOnArray(mtmAUD, 0, firstR, 0, MODE_EMA, i);
smoothAbs1AUD[i] = iMAOnArray(mtmAbsAUD, 0, firstR, 0, MODE_EMA, i);
smooth2AUD[i] = iMAOnArray(smooth1AUD, 0, (uchar)MathFloor(firstR / divPeriod), 0, MODE_EMA, i);
smoothAbs2AUD[i] = iMAOnArray(smoothAbs1AUD, 0, (uchar)MathFloor(firstR / divPeriod), 0, MODE_EMA, i);
if(smoothAbs2AUD[i] == 0)
{
Print("Zero divide of 2nd smoothing at index: #" + (string)i);
continue;
}
aud[i] = 100 * smooth2AUD[i] / smoothAbs2AUD[i];
}
Image attached shows the indicator (only with AUD plotted) which is different on two charts.
I've tryed on demo and backtest but nada... :-(

Is it possible to draw an indicator using EA

Say when using EA, and when I detect the current bar has the lowest price I have seen so far, I will draw a horizontal line below it, is it possible to achieve with EA rather than custom indicator?
yes, if your conditions are met - you can draw a horizontal line or trend line or update its price parameters. in order to draw a line, use the following function, for moving - ObjectSetDouble():
bool HLineCreate(const long chart_ID=0, // chart's ID
const string name="HLine", // line name
const int sub_window=0, // subwindow index
double price=0, // line price
const color clr=clrRed, // line color
const ENUM_LINE_STYLE style=STYLE_SOLID, // line style
const int width=1, // line width
const bool back=false, // in the background
const bool selection=true, // highlight to move
const bool hidden=true, // hidden in the object list
const long z_order=0) // priority for mouse click
{
//--- if the price is not set, set it at the current Bid price level
if(!price)
price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
//--- reset the error value
ResetLastError();
//--- create a horizontal line
if(!ObjectCreate(chart_ID,name,OBJ_HLINE,sub_window,0,price))
{
Print(__FUNCTION__,
": failed to create a horizontal line! Error code = ",GetLastError());
return(false);
}
//--- set line color
ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
//--- set line display style
ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);
//--- set line width
ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,width);
//--- display in the foreground (false) or background (true)
ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
//--- enable (true) or disable (false) the mode of moving the line by mouse
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);
//--- hide (true) or display (false) graphical object name in the object list
ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);
//--- set the priority for receiving the event of a mouse click in the chart
ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
//--- successful execution
return(true);
}

How to count how many bars back the moving average crossed last?

I am writing an MQL4 Custom Indicator that would tell how many bars ago a particular set of moving averages crossed.
To be specific, I want the output to show me that
"The 20 period MA( .. PRICE_OPEN ) is below MA( .. PRICE_CLOSE ) for the past 10 bars".
in a form of an int number.
double ma1 = iMA( Symbol(), 10080, 20, 0, 0, PRICE_OPEN, 0 );
double ma2 = iMA( Symbol(), 10080, 20, 0, 0, PRICE_CLOSE, 0 );
I want to find out that ma1 has been above or below ma2 for how many bars since the current bar.
TLDR;
MQL4 Custom Indicator design is a bit schizophrenic task with 2 parts
Once decided to design a Custom Indicator, one has to pay attention to both sides of the coin.
MQL4 code has a non-trivial signature for a caller-side( normally an
Expert Advisor or a Script type of MQL4 code )and
Custom Indicator, on it's own, operates in a special mode, where it calculates & store it's values into one or more arrays called IndexBuffer-s.
Phase I.: design Caller-side interface ( what all do we need to set / receive ? )
Phase II.: design Custom Indicator implementation side
This way, the given indicator shall
set just one parameter: a period ... 20
receive just one int a value of days { +:above | 0: xoss | -: under }
Phase I. : Design Caller-side interface
Given the above parametrisation and the MQL4 concept of Custom Indicator construction, the following universal template ( recommended to be routinely maintained as a commented section right in CustomIndicator-file ) reflects details needed for the Caller-side interface ( which one benefits from by just a copy-paste mechanics, while the implementation-integrity responsibility is born by the CustomIndicator maintainer ):
// CALL-er SIDE INTERFACE |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//
// #define sIndicatorPathNAME "#AliceInTheWonderlands_msMOD_0.00"
//
// //_____________________________________INPUT(s)
// // iMA_PERIOD:
// extern int iMA_PERIOD = 20;
//
// //_____________________________________OUTPUT(s):
// #define iOutputDoubleUpOrDnBUFFER 0
//
// //_____________________________________CALL-SIGNATURE:
//
// double iCustom( _Symbol, // string symbol, // symbol: Symbol name on the data of which the indicator will be calculated. NULL means the current symbol.
// PERIOD_W1, // int timeframe, // timeframe
// sIndicatorPathNAME, // string name, // path/name of the custom indicator compiled program: Custom indicator compiled program name, relative to the root indicators directory (MQL4/Indicators/). If the indicator is located in subdirectory, for example, in MQL4/Indicators/Examples, its name must be specified as "Examples\\indicator_name" (double backslash "\\"must be specified as separator instead of a single one).
// iMA_PERIOD, // ...[1] ..., // custom indicator [1]-st input parameter
// <<N/A>>, // ...[2+] // custom indicator further input parameters (if necessary)
// iOutputDoubleUpOrDnBUFFER, // int mode, // line index: Line index. Can be from 0 to 7 and must correspond with the index, specified in call of the SetIndexBuffer() function.
// i // int bar_shift // shift
// );
// ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Typical Expert Advisor call looks like this:
int anIndicatorVALUE = iCustom( _Symbol,
PERIOD_W1,
sIndicatorPathNAME,
iMA_PERIOD,
iOutputDoubleUpOrDnBUFFER,
aCurrentBarPTR
);
if ( 0 > anIndicatorVALUE ) // on "line" under for <anIndicatorVALUE> [PERIOD]-s
{ ...
}
Phase II. : Design Custom Indicator implementation side
Preset an overall capacity
#property indicator_buffers 1 // compile time directive
Allocate memory for IndexBuffer(s) needed
IndicatorBuffers( 1 ); // upper limit of 512 is "far" enough
Declare IndexBuffer
double UpOrDnBUFFER[]; // mandatory to be a double
Associate IndexBuffer with an index
SetIndexBuffer( 0, UpOrDnBUFFER ); // index 0: UpOrDnBUFFER[]
ArraySetAsSeries( UpOrDnBUFFER, True ); // reverse AFTER association
The core logic of any Custom Indicator is inside an OnCalculate() function, where you
- implement desired calculusand- store resulting values into respective cells of the UpOrDnBUFFER[];
Writing a fully fledged code upon request is not the key objective of StackOverflow, but let me sketch a few notes on this, as Custom Indicator implementation side requires a bit practice:
because Custom Indicator OnCalculate() operates "progressively", so do design your calculation strategy & keep in mind the state-less-ness between "blocks" of forward-progressive blocks of calculations.
as given, crosses are tri-state system, so watch issues on cases, where decisions are made on a "live" bar, where the state { above | cross | under } may change many times during the bar-evolution.

Collision detection not working on all sides of UIImageView

This code is used to test my collisions but it only works on 2 sides on each UIImageView. Why doesn't it work for 3 sides? It'll never work on top side and bottom side of UIImageView or left and right. It only works top and left or top and right or bottom and right or bottom and left. Why is that? How do i make it work on at least 3 sides?
int moveX;
int moveY;
viewController.h{
IBOutlet UIImageView *circle;
NSTimer *time;
IBOutlet UIImageView *platform;
}
-(IBAction)StartGame:(id)sender;
-(void)moveCircle;
-(void)bounce;
#end
viewcontroller.m
-(IBAction)StartGame:(id)sender{
start.hidden=YES;
time=[NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(moveCircle) userInfo:nil repeats:YES];
circleX=arc4random()%11;
circleX=circleX-5;
circleY=arc4random()%11;
circleY=circleY-5;
circleX=arc4random()%11;
circleX=circleX+5;
circleY=arc4random()%11;;
circleY=circleY+5;
if (circleX==0) {
circleX=1;
}
if (circleY==0) {
circleY=1;
}
-(void)moveCircle{
circle.center=CGPointMake(circle.center.x -circleX, circle.center.y -circleY);
if (circle.center.x <30) {
circleX=0-circleX;
}
if (circle.center.x >320) {
circleX=0-circleX;
}
if (circle.center.y <28 ) {
circleY=0-circleY;
}
if (circle.center.y >568) {
circleY=0-circleY;
}
[self bounce];
}
-(void)bounce{
if (CGRectIntersectsRect(circle.frame, platform.frame)) {
circleY=arc4random()%5;
circleY=0+circleY;
circleX=arc4random()%5;
circleX=0+circleX;
circleY=arc4random()%5;
circleY=0-circleY;
circleX=arc4random()%5;
circleX=0-circleX;
}
}
I see no sense at all in your question but let's look at your code:
Assuming you start with StartGame which hopefully happens only once:
You create a timer (this should be invalidated when the game ends but no matter at the moment)
you set circleX and circleY to some random position between -5 and 5
you set circleX and circleY to some random position between 5 and 15 (again?!)
you check if circleX and circleY are at 0 and set them to 1 (this is highly unlikely to happen due to the previous statement)
Then move circle:
you add to the circle center the circleX and circleY values where then I assume those 2 values are actually speed. (so far so good)
you check the circle position to be out of borders and negate appropriate speed. (This hardcoded values are a bit strange but ok for now I guess)
You call bounce
And the bounce method:
You check if circle and platform frames intersect. (O.K. I guess)
You set a random speed to circleX and circleY from 0 to 4
You set a random speed to circleX and circleY from 0 to -4 (again?!)
So what I can assume from this code is you have 2 views on your view controller where one is a static obstacle called platform and the other is a moving circle. The circle itself will be moving to some direction depending on some random values from the StarGame method and correctly bouncing off the hardcoded borders. If the circle intersects with the platform the circle will change speed to some random negative values from -4 to 0 in both X and Y.
So what is the issue here exactly? The bouncing off the platform will make the circle move toward your top-left corner of the screen no matter which side does the circle hit the platform from. Maybe you need some if statements in your bounce method to find a correct value for the new speed depending on which side is the platform hit from.

Animating rotation changes of UIImageView

I'm making an app that (among other things) displays a simplified compass image that rotates according to the device's rotation. The problem is that simply doing this:
float heading = -1.0f * M_PI * trueHeading / 180.0f; //trueHeading is always between 0 and 359, never 360
self.compassNeedle.transform = CGAffineTransformMakeRotation(heading);
inside CLLocationManager's didUpdateHeading method makes the animation ugly and choppy.
I have already used Instruments to find out whether its simply my app not being able to render at more than 30-48 fps, but that's not the case.
How can I smooth out the image view's rotation so that it's more like Apple's own Compass app?
Instead of using the current instant value, try using the average of the last N values for the true heading. The value may be jumping around a lot in a single instant but settle down "in the average".
Assuming you have a member variable storedReadings which is an NSMutableArray:
-(void)addReading(float):newReading
{
[storedReadings addObject:[NSNumber numberWithFloat:newReading]];
while([storedReadings count] > MAX_READINGS)
{
[storedReadings removeObjectAtIndex:0];
}
}
then when you need the average value (timer update?)
-(float)calcReading
{
float result = 0.0f;
if([storedReadings count] > 0)
{
foreach(NSNumber* reading in storedReadings)
{
result += [reading floatValue];
}
result /= [storedReadings count];
}
return result;
}
You get to pick MAX_READINGS a priori.
NEXT LEVEL(S) UP
If the readings are not jumping around so much but the animation is still choppy, you probably need to do something like a "smooth" rotation. At any given time, you have the current angle you are displaying, theta (store this in your class, start it out at 0). You also have your target angle, call it target. This is the value you get from the smoothed calcReading function. The error is defined as the difference between the two:
error = target-theta;
Set up a timer callback with a period of something like 0.05 seconds (20x per second). What you want to do is adjust theta so that the error is driven towards 0. You can do this in a couple of ways:
thetaNext += kProp * (target - theta); //This is proportional feedback.
thetaNext += kStep * sign(target-theta); // This moves theta a fixed amount each update. sign(x) = +1 if x >= 0 and -1 if x < 0.
The first solution will cause the rotation to change sharply the further it is from the target. It will also probably oscillate a little bit as it swings past the "zero" point. Bigger values of kProp will yield faster response but also more oscillation. Some tuning will be required.
The second solution will be much easier to control...it just "ticks" the compass needle around each time. You can set kStep to something like 1/4 degree, which gives you a "speed" of rotation of about (1/4 deg/update) * (20 updates/seconds) = 5 degrees per second. This is a bit slow, but you can see the math and change kStep to suit your needs. Note that you may to "band" the "error" value so that no action is taken if the error < kStep (or something like that). This prevents your compass from shifting when the angle is really close to the target. You can change kStep when the error is small so that it "slides" into the ending position (i.e. kStep is smaller when the error is small).
For dealing with Angle Issues (wrap around), I "normalize" the angle so it is always within -Pi/Pi. I don't guarantee this is the perfect way to do it, but it seems to get the job done:
// Takes an angle greater than +/- M_PI and converts it back
// to +/- M_PI. Useful in Box2D where angles continuously
// increase/decrease.
static inline float32 AdjustAngle(float32 angleRads)
{
if(angleRads > M_PI)
{
while(angleRads > M_PI)
{
angleRads -= 2*M_PI;
}
}
else if(angleRads < -M_PI)
{
while(angleRads < -M_PI)
{
angleRads += 2*M_PI;
}
}
return angleRads;
}
By doing it this way, -pi is the angle you reach from going in either direction as you continue to rotate left/right. That is to say, there is not a discontinuity in the number going from say 0 to 359 degrees.
SO PUTTING THIS ALL TOGETHER
static inline float Sign(float value)
{
if(value >= 0)
return 1.0f;
return -1.0f;
}
//#define ROTATION_OPTION_1
//#define ROTATION_OPTION_2
#define ROTATION_OPTION_3
-(void)updateArrow
{
// Calculate the angle to the player
CGPoint toPlayer = ccpSub(self.player.position,self.arrow.position);
// Calculate the angle of this...Note there are some inversions
// and the actual image is rotated 90 degrees so I had to offset it
// a bit.
float angleToPlayerRads = -atan2f(toPlayer.y, toPlayer.x);
angleToPlayerRads = AdjustAngle(angleToPlayerRads);
// This is the angle we "wish" the arrow would be pointing.
float targetAngle = CC_RADIANS_TO_DEGREES(angleToPlayerRads)+90;
float errorAngle = targetAngle-self.arrow.rotation;
CCLOG(#"Error Angle = %f",errorAngle);
#ifdef ROTATION_OPTION_1
// In this option, we just set the angle of the rotated sprite directly.
self.arrow.rotation = CC_RADIANS_TO_DEGREES(angleToPlayerRads)+90;
#endif
#ifdef ROTATION_OPTION_2
// In this option, we apply proportional feedback to the angle
// difference.
const float kProp = 0.05f;
self.arrow.rotation += kProp * (errorAngle);
#endif
#ifdef ROTATION_OPTION_3
// The step to take each update in degrees.
const float kStep = 4.0f;
// NOTE: Without the "if(fabs(...)) check, the angle
// can "dither" around the zero point when it is very close.
if(fabs(errorAngle) > kStep)
{
self.arrow.rotation += Sign(errorAngle)*kStep;
}
#endif
}
I put this code into a demo program I had written for Cocos2d. It shows a character (big box) being chased by some monsters (smaller boxes) and has an arrow in the center that always points towards the character. The updateArrow call is made on a timer tick (the update(dt) function) regularly. The player's position on the screen is set by the user tapping on the screen and the angle is based on the vector from the arrow to the player. In the function, I show all three options for setting the angle of the arrow:
Option 1
Just set it based on where the player is (i.e. just set it).
Option 2
Use proportional feedback to adjust the arrow's angle each time step.
Option 3
Step the angle of the arrow each timestep a little bit if the error angle is more than the step size.
Here is a picture showing roughly what it looks like:
And, all the code is available here on github. Just look in the HelloWorldLayer.m file.
Was this helpful?

Resources